Homgar integration

HomGar Home Assistant Integration

hacs_badge

Home Assistant integration for HomGar / RainPoint / Diivoo devices.

This project is a stabilized and extended version of the HomGar integration, specifically optimized for irrigation timers and weather stations.


:rocket: Key Features

  • Real-Time Irrigation Timers: Supports second-by-second countdowns for zones using native Home Assistant TIMESTAMP sensors.
  • MQTT-Powered Responsiveness: Uses a virtual observer pattern to capture device status updates instantly via MQTT.
  • Smart Synchronization: Advanced multi-zone tracking for Diivoo and RainPoint timers with “Last Device Sync” sensors to monitor network health.
  • Weather Station Support: Full decoding for hierarchical weather hub data (model 257/85/86).

:globe_showing_europe_africa: Supported Devices

Model Description Role
Diivoo WT-11W 3-Zone Smart Water Timer Irrigation Control
HTV405FRF 4-Zone Smart Water Timer Irrigation Control
257 / 85 / 86 HomGar Weather Setup Temp, Humidity, Pressure
HCS026FRF Soil Moisture Sensor Garden Monitoring
HCS012ARF Rain Sensor Precipitation Monitoring

:hammer_and_wrench: Debugging & Protocol Analysis

If you have a new HomGar device that isn’t fully supported yet, you can help by providing debug logs.

1. Enable Debug Logging

Add the following to your configuration.yaml:

logger: default: info logs: custom_components.homgar: debug

2. Identifying Protocol Patterns

Look for log entries starting with 11#. These contain the raw Hexadecimal status of your device.

Common Hex Markers:

  • B7: Countdown timer end-time. The 4 bytes following B7 represent the “master clock” ticks when the valve will close.
  • AD: Duration setting. The bytes after AD indicate the configured watering duration.
  • D8: Status code.
    • D841 → Valve is ON.
    • D820 / D800 → Valve is OFF.
  • FEFF0F: Master Clock marker. The 4 bytes following this are the current device “ticks” (seconds since boot/sync).

Byte Order:

Values are encoded in Little-Endian. Example: DC05 in hex is 05DC in decimal (1500 seconds).

1 Like

Nice to see a integration, i have the Diivoo WT-11W, but can’t add this to Home Assistant. Can’t even find the device in the original Smart Life app by Tuya.
How can i get this working inside Home Assistant? I am missing the instructions?

Go to HACS in Home Assistant, click the 3 dots in the top-right hand corner, select custom repositories, paste in the URL (GitHub - macher91/homgar-homeassistant: HomGar API - Home Assistant integration ¡ GitHub) and select integration as the type.
Then go back to HACS, search for HomGar, install it, and then find the integration in the Home Assistant integrations directory.

1 Like

Hi @macher91, awesome that you are attempting to tackle the HomGar gap with HomeAssistant at the moment, thank you.

I’m using a WT-11W. The switch status was not feeding back but since updating the firmware version to 1.1.1038 via the HomGar app it appears to be working correctly now.

The integration does have to be disabled to use the HomGar app, as it sees the login as already in use and boots you out.

Hi

I have a WT-09W and this does not seem to work in the app. Any idea what needs to be done to make it compatible with home assistant?

Thanks

Hey everyone I was able to revere engineer the complete rf protocol and made an alternative software for the original gateway. Feel free to test the first release ^^
I put everything into a Homeassistant App/Integration :smiley:

I posted more infos here: Diivoo / Tuya / Smart Home Hose Water Valve - #11 by Stefan_G

Github: GitHub - Technerd-SG/hassio-diivoo2mqtt ¡ GitHub

2 Likes

Got it, never expected it too work so simple, thanks!

its not tuya, gray/green use diffrent protocol. You can use HomGar. I will try to add WT-11W but I need help to do this. I will ping you later

There is another option. Create technical account (you need use another email or if you have gmail you can add suffix like [email protected]). Than you can share your home with technical account and you can use home assistant integration along with homgar appa

Amazing :blush:
I gave up when I was trying to remove the screws from the gate. I even tried drilling through it, but I didn’t want to damage it too much.

If someone doesn’t want to bother with removing these screws or programming the ESP32, they’ll have a choice now.
I can only recommend @Stefan_G’s solution, and if it had been created earlier, I wouldn’t have even created my integration :slight_smile:

I need debug logs from HA:
add in configuration.yaml:
logger:
default: info
logs:
custom_components.homgar: debug

Hey.
I have bougt a WT-09W thinking that it worked like my old Diivoo 1 valve through tuya.
Ofcourse it doesnt =)
But I have run some logs on this device if that might help.

Device info

Field Value

Hub model: HWG0538WRF (modelCode 256)
Subdevice model: HTV0537FRF (modelCode 270)

Subdevice name in app: WT-09W
Integration version: 1.1.0
HA version: 2026.4.3

Description

The integration discovers the hub and subdevice correctly and MQTT is fully working, but the subdevice HTV0537FRF (modelCode 270) is unsupported so no entities are created.

I captured debug logs with confirmed, timestamped valve actions for both zones to help map the protocol.


Confirmed hex captures (D01 field)

Idle (both zones off):

11#17E1B90019D8001AD8211D201E2018DC0121B70000000022B781DB4E1925AD000026AD5802FEFF0F00D94E19

Zone 1 ON:

11#17E1BC0019D8201AD8001D201E2018DC0121B70000000022B70000000025AD580226AD0000FEFF0F08DA4E19

Zone 1 OFF:

11#17E1BB0019D8001AD8001D201E2018DC0121B70000000022B70000000025AD000026AD0000FEFF0F0EDA4E19


**Zone 2 ON:**

11#17E1B90019D8001AD8201D201E2018DC0121B70000000022B70000000025AD000026AD5802FEFF0F49D94E19


**Zone 2 OFF:**

11#17E1B80019D8001AD8001D201E2018DC0121B70000000022B70000000025AD000026AD0000FEFF0F4FD94E19


**Preliminary observations**

Looking at the `D8` status bytes, a pattern is visible:

* `D821` = idle
* `D820` = zone ON
* `D800` = zone OFF

Zone 1 and Zone 2 appear to occupy different positions in the hex stream, making them distinguishable. Happy to provide additional captures or the raw logs if needed (e.g. both zones simultaneously, or runs with specific durations se

Tried it myself =)
Probably not perfect but it works with the WT-09W 2-Zone Water Timer (modelCode 270)


Changes to devices.py:

  1. Add this class before class HWG0538WRF:
class DiivooWT09W(DiivooWT11W):
    """Diivoo WT-09W 2-Zone Water Timer (HTV0537FRF, modelCode 270)."""
    MODEL_CODES = [270]
    FRIENDLY_DESC = "Diivoo WT-09W 2-Zone Water Timer"

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # Only 2 zones, different port addresses than WT-11W
        self.zones = {
            1: {"active": False, "status": "off_idle", "countdown_timer": 0, "countdown_end_time": None, "duration_setting": 0},
            2: {"active": False, "status": "off_idle", "countdown_timer": 0, "countdown_end_time": None, "duration_setting": 0},
        }

    def _parse_port_statuses_precise(self, hex_data):
        """WT-09W uses 19D8 (zone 1) and 1AD8 (zone 2)."""
        status_map = {'D821': 'on', 'D820': 'off_recent', 'D800': 'off_idle'}
        port_patterns = ['19D8', '1AD8']
        for port_num, pattern in enumerate(port_patterns, 1):
            pattern_pos = hex_data.find(pattern)
            if pattern_pos >= 0 and pattern_pos + 6 <= len(hex_data):
                status_hex = hex_data[pattern_pos + 2:pattern_pos + 6]
                if status_hex in status_map:
                    self.zones[port_num]['active'] = status_map[status_hex] == 'on'
                    self.zones[port_num]['status'] = status_map[status_hex]
                    logger.debug("WT09W Zone %d status: %s", port_num, status_map[status_hex])
                else:
                    logger.warning("WT09W Zone %d unknown status hex: %s", port_num, status_hex)

    def _parse_countdown_timers_precise(self, hex_data, current_ticks, msg_time=None):
        """WT-09W uses 21B7 (zone 1) and 22B7 (zone 2)."""
        timer_patterns = ['21B7', '22B7']
        for port_num, pattern in enumerate(timer_patterns, 1):
            pattern_pos = hex_data.find(pattern)
            if pattern_pos >= 0 and pattern_pos + 12 <= len(hex_data):
                timer_hex = hex_data[pattern_pos + 4:pattern_pos + 12]
                try:
                    end_ticks = int.from_bytes(bytes.fromhex(timer_hex), "little")
                    if end_ticks > current_ticks > 0:
                        rem_s = end_ticks - current_ticks
                    else:
                        rem_s = 0
                        if self.zones[port_num]['active']:
                            self.zones[port_num]['active'] = False
                            self.zones[port_num]['status'] = 'off_idle'
                    self.zones[port_num]['countdown_timer'] = rem_s
                    if rem_s > 0:
                        base_time = msg_time if msg_time else datetime.now().timestamp()
                        new_end_time = base_time + rem_s
                        old_end_time = self.zones[port_num].get('countdown_end_time')
                        if not old_end_time or abs(new_end_time - old_end_time) > 10:
                            self.zones[port_num]['countdown_end_time'] = new_end_time
                    else:
                        self.zones[port_num]['countdown_end_time'] = None
                except ValueError as e:
                    logger.debug("WT09W Zone %d timer error: %s", port_num, e)

    def _parse_duration_settings_precise(self, hex_data):
        """WT-09W uses 25AD (zone 1) and 26AD (zone 2)."""
        duration_patterns = ['25AD', '26AD']
        for port_num, pattern in enumerate(duration_patterns, 1):
            pattern_pos = hex_data.find(pattern)
            if pattern_pos >= 0 and pattern_pos + 8 <= len(hex_data):
                duration_hex = hex_data[pattern_pos + 4:pattern_pos + 8]
                try:
                    self.zones[port_num]['duration_setting'] = int.from_bytes(
                        bytes.fromhex(duration_hex), "little")
                except ValueError:
                    self.zones[port_num]['duration_setting'] = 0

    def control_zone(self, api, zone_number, mode, duration=0):
        """Control zone 1 or 2."""
        if zone_number not in [1, 2]:
            raise ValueError("Zone number must be 1 or 2")
        return api.control_device_work_mode(
            device_name=self.hub_device_name,
            product_key=self.hub_product_key,
            mid=str(self.mid),
            addr=self.address,
            port=zone_number,
            mode=mode,
            duration=duration
        )

    def __str__(self):
        s = super(HomgarSubDevice, self).__str__()
        active_zones = [str(z) for z in [1, 2] if self.is_zone_active(z)]
        if active_zones:
            s += f" [Active zones: {', '.join(active_zones)}]"
        return s
  1. Add DiivooWT09W to MODEL_CODE_MAPPING:
MODEL_CODE_MAPPING = {
    code: clazz
    for clazz in (
        RainPointDisplayHub,
        RainPointSoilMoistureSensor,
        RainPointRainSensor,
        RainPointAirSensor,
        RainPoint2ZoneTimer,
        DiivooWT11W,
        DiivooWT09W,        # ← added
        HWG0538WRF,
        HomgarWeatherHub,
        HomgarWeatherStation,
        HomgarIndoorSensor,
        HTV405FRF
    ) for code in clazz.MODEL_CODES
}

Changes to switch.py:

  1. Update the import line:
from .devices import DiivooWT11W, DiivooWT09W, RainPoint2ZoneTimer, HWG0538WRF, HTV405FRF
  1. Update the device setup loop — check DiivooWT09W before DiivooWT11W:
    for device_id, device in coordinator.devices.items():
        if isinstance(device, DiivooWT09W):  # must check before DiivooWT11W
            for zone in [1, 2]:
                entities.append(
                    HomgarZoneSwitch(coordinator, device_id, device, zone)
                )
        elif isinstance(device, DiivooWT11W):
            for zone in [1, 2, 3]:
                entities.append(
                    HomgarZoneSwitch(coordinator, device_id, device, zone)
                )
        elif isinstance(device, RainPoint2ZoneTimer):
            for zone in [1, 2]:
                entities.append(
                    HomgarZoneSwitch(coordinator, device_id, device, zone)
                )
        elif isinstance(device, HTV405FRF):
            for zone in [1, 2, 3, 4]:
                entities.append(
                    HomgarZoneSwitch(coordinator, device_id, device, zone)
                )

Thanks, Please check new version 1.2.0 if its fine now

1.3.0 released. 1, 2, 3, 4 zones devices should work now

2 Likes

Thanks for your efforts !
The 2 valve one works great with your latest update as well =)

1 Like

Hi @macher91,
Thanks a lot for this fantastic integration. I was wondering whether the integration works locally or if it requires an internet connection.

It required internet connection, only way to get it locally is Homgar integration - #6 by Stefan_G

I'm trying to figure out why I can't connect. I'm in the US and attempting to use the email and password that I use to log into the RainPoint Home app. First off, it's not clear what I should put in the area code (if it even matters). Error log is pasted below...
Logger: custom_components.homgar.api
Source: custom_components/homgar/api.py:131
Integration: homgar (documentation, issues)
First occurred: 2:54:03 PM (6 occurrences)
Last logged: 4:08:42 PM

HomGar API error code 2001 on POST /auth/basic/app/login with kwargs: {'json': {'areaCode': '33', 'phoneOrEmail': '[email protected]', 'password': 'c50eda3ac15433f6ecc62ac740020c8b', 'deviceId': '3601615f428b6d3364bd31bd397e6b7b'}, 'with_auth': False} | response: {'code': 2001, 'msg': 'Wrong account or password', 'data': None, 'ts': 1781122122766}

Seems like your password is incorrect. Try logging out and back on throug the app. If it works, log off then try the integration. I don<t think the area code is required. I left it default. Works well on my end.