Irrigation Hunter X-Core remote control using REM pin

Hi!

I have a Hunter X-Core irrigation controller (8 zones) and I wanted to integrate it into homeassistant for better control.
I had planned to add a relay to open the valves and bypass the controller (turned off) but I found out that there is a pin exposed for remote control.
I found this project and I want to build it using ESPHome.
https://www.loullingen.lu/projekte/Hunter/index.php?language=EN

Can anybody point me to the right direction?
One idea is to import the ardruino files and write the YAML for custom switches.
I have almost none programming knowledge so there might be an easier way to accomplish what I want that I don’t know about.

Thank you!

Have a read of this and see if you can make sense of it:

https://esphome.io/custom/custom_component.html

1 Like

This is where I started!

My first problem is that the ardruino files have no class but only void.
So I started from here
https://www.w3schools.com/cpp/cpp_classes.asp

but I think I need to start earlier!

I think I need to define a (or more) class.
The available functions are the following. I only need to operate through a switch HunterStop, HunterStart and HunterProgram. The rest is internal as I understand it.


void HunterStop(byte zone);

void HunterStart(byte zone, byte time);

void HunterProgram(byte num);

void HunterBitfield(vector <byte>& bits, byte pos, byte val, byte len);

void HunterWrite(vector<byte> buffer, bool extrabit);

void HunterLow(void);

void HunterHigh(void);


I have spent several hours with no result.
I don’t know anything about C++ so I will try to create custom switches in HomeAssistant.

Any idea how to import this API?

2 Commands
START
GET /api/start/zone/[ZONE-NUMBER]?time=[TIME-MINUTES]&result=[WEBHOOK]
STOP
GET /api/stop/zone/[ZONE-NUMBER]?result=[WEBHOOK]

Don’t use ESPHome for this HTTP: GET function.
Use Home Assistant rest_command: instead.
This goes in your configuration.yaml file.

rest_command:
  start_irrigation:
    url:'http://{{ target }}/api/start/zone/{{ zone_number}}?time={{ time_minutes }}&result={{ webhook }}
    method: 'GET'
    content_type: 'text/plain'
  stop_irrigation:
    url:'http://{{ target }}/api/stop/zone/{{ zone_number}}&result={{ webhook }}
    method: 'GET'
    content_type: 'text/plain'

Then you can use the service: item in your automations as

    service: rest_command.start_irrigation

You’ll need to use a service_template definition to pass the values for target , zone_number , and webhook.

  action:
  - service_template: rest_command.start_irrigation
  - data_template: 
    target: "192.168.1.123"
    zone_number: "3"
    time_minutes: "8" 
    webhook:  "http://i_dont_know/something/something_else"
1 Like

Thank you very much for your time.
I successfully found a similar solution two nights ago.

switch:
  - platform: template
    switches:
      hunterirrigation:
        friendly_name: "Irrigation On/Off"
        turn_on:
            -  service: rest_command.startzone
        turn_off:
            -  service: rest_command.stopzone
        icon_template: >-
               {% if is_state('switch.hunterirrigation', 'on') %}
               mdi:water-pump
               {% else %}
               mdi:water-pump-off
               {% endif %}

rest_command:
  startzone:
    url: "http://192.168.1.199/api/start/zone/{{ states('input_select.zonenumber') }}?time={{states('input_number.zone_' ~ states('input_select.zonenumber') ~ '_timer')}}"
  stopzone:
    url: "http://192.168.1.199/api/stop/zone/{{ states('input_select.zonenumber') }}"

And I use 2 helpers
input_select.zonenumber = Dropdown menu to select specific zone to water
input_number.zone_1 - 6_timer = Where I have 6 sliders to select time for each zone.

I think your approach is more clever but didn’t know how to send data through your service.

rest_command.start_irrigation

Anyway, it’s working and I learned some things about templates for next time :slight_smile:

My only problem with the above is the icon change because it errors with

Template loop detected while processing event:

I think I could either set a variable or add the change of the icon in the Automation I have set to “turn off” the irrigation only in HomeAssistant like a timer (as the interaction with the hunter controller is one-way).

- id: '1626945764401'
  alias: Irrigation timer
  description: ''
  trigger:
  - platform: state
    entity_id: switch.hunterirrigation
    from: 'off'
    to: 'on'
  condition: []
  action:
  - delay:
      hours: 0
      minutes: '{{ states(''input_number.zone_'' ~ states(''input_select.zonenumber'')
        ~ ''_timer'') | int }}'
      seconds: 0
      milliseconds: 0
  - service: switch.turn_off
    target:
      entity_id: switch.hunterirrigation
  mode: single

What’s the final code that you used on the esp module? Did you use esphome?

I used this firmware (so I didn’t use esphome)
GitHub - ecodina/hunter-wifi at 62561864c56152a0d479432be79e6be09c0ff853

and I used the code above to use connect Homeassistant through the ready API of this firmware.

It’s super easy and it’s rock solid.

1 Like

I tested and it works perfectly!
now all i want to add is the support to the webhook to get a result.

i’m thinking to use the trigger webhook to either send me a notification, or to move the switch back to off (if i start) or on (if i stop) the switch.

In case you wanted to give it a shot, i forked that repository and i added MQTT support :slight_smile:

i’m still adding some minor tuning but here is the code - https://github.com/anubisg1/hunter-wifi

adds MQTT, mDNS and OTA support (i had to temporarily drop the captive portal though until i figure out how to get MQTT subscription work with that Async Wifi Manager.

i also fixed an issue in the API webhook response so that now HA can understand it

This automation for example would get the webhook from the API call and use it to send a phone notification:

alias: test
description: ''
trigger:
  - platform: webhook
    webhook_id: zone_start
    id: zone_start
condition: []
action:
  - service: notify.mobile_app_sm_g965f
    data:
      message: '{{ trigger.json.action }} Ended with {{ trigger.json.result }}'
mode: single

For MQTT the topics are

/hunter/$(hostname)/results 
/hunter/$(hostname)/CheckIn
/hunter/$(hostname)/zone/$(zone_number)
/hunter/$(hostname)/program/$(program_number)

the results are sent to the result topic, and the zone/program topics expect the following data

{ "action" : "start" , "time" : 1 } --> send to zone topic to start the zone for 1 minute
{ "action" : "stop" } --> send to zone topic to stop the zone
{ "action" : "start" } --> sent to program topic to start a program

if you try it, let me know what you think about it :slight_smile:

4 Likes

Thanks for the fork! I was just getting started with the ecodina branch and then saw your post. It appears that the wifi config is held over from my prior ecodina flash… (does that sound correct?) Also, how do you config the MQTT host/username/pass?

You need to flash by erasing firmware and file system content.

All customization is done here, until I get the captive portal back in

1 Like

@anubisg1 I saw pull requests from you in the GitHub - ecodina/hunter-wifi at 62561864c56152a0d479432be79e6be09c0ff853
I saw also that your fork is 2 commits ahead and 19 commits behind of the ecodina github repo.

Which github repo is now the one to take?

I fetched and merged his new changes. Really they were all about documentation.

That said, I’m trying to stay as close as possible to ecodina’s repository as everything I do I want to merge there.

The only difference between his and my repository today is that I have added OTA upgrades support. While working, I wanted to still refine it a little before sending it to him.

1 Like

Noob question…
How do you guys integrate the mqtt topics into Home Assistant? Does anyone have an example?

I’m looking for a way to add switches and time selectors for the irrigation zones.

It took me sometime but I figured it out. Will share my configuration here for other benefit from.

My aim was to be able to select a zone and start the irrigation for a adjustable period of time. Next to that I wanted it to be flexible enough to be used in an automation in the future.

The UI elements:

The script (in Dutch)

##################################
# Irrigatie zone selectbox       #
##################################
input_select:
  irrigatie_zone_achtertuin:
    name: Irrigatie zone achtertuin
    options:
      - Druppel slang
      - Sproeiers

template:
  - sensor:
    - name: irrigatie_map_zone_achtertuin
      state: >
        {% set mapper =
          { 'Druppel slang':'1',
            'Sproeiers':'2' } %}
        {% set state = states('input_select.irrigatie_zone_achtertuin') %}
        {% set id = mapper[state] if state in mapper %}
        {{ id }}

input_text:
  irrigatie_actieve_zone_achtertuin:
    initial: inactive

##################################
# Irrigatie zone tijd selectie   #
##################################
input_number:
  irrigatie_timer_1_achtertuin:
    name: Timer druppel slang
    initial: 60
    min: 1
    max: 120
    step: 1

  irrigatie_timer_2_achtertuin:
    name: Timer sproeiers
    initial: 45
    min: 1
    max: 120
    step: 1

################################################
# Geselecteerde Sproeier switch script & timer #
################################################
timer:
  irrigatie_tijd_resterend_achtertuin:
    name: Resterende tijd
    duration: "00:00:00"

switch:
  - platform: template
    switches:
      irrigatie_achtertuin:
        friendly_name: "Sproeier aan/uit"
        turn_on:
          - service: script.turn_on
            data:
              entity_id: script.irrigatie_aan_achtertuin
          - service: timer.start
            target:
              entity_id: timer.irrigatie_tijd_resterend_achtertuin
            data:
              duration: "{{ states('input_number.irrigatie_timer_' ~ states('sensor.irrigatie_map_zone_achtertuin') ~ '_achtertuin') | int  * 60 }}"
          - service: input_text.set_value
            target:
              entity_id: input_text.irrigatie_actieve_zone_achtertuin
            data:
              value: "{{ states('sensor.irrigatie_map_zone_achtertuin') }}"
        turn_off:
          - service: script.turn_on
            data:
              entity_id: script.irrigatie_uit_achtertuin
          - service: timer.finish
            target:
              entity_id: timer.irrigatie_tijd_resterend_achtertuin
          - service: input_text.set_value
            target:
              entity_id: input_text.irrigatie_actieve_zone_achtertuin
            data:
              value: inactive
        icon_template: >-
               {% if is_state('switch.hunterirrigation', 'on') %}
               mdi:water-pump
               {% else %}
               mdi:water-pump-off
               {% endif %}

##################################
# Aanroepen naar mqtt            #
##################################
script:
  irrigatie_aan_achtertuin:
    alias: Sproeier aan
    sequence:
    - service: mqtt.publish
      data_template:
        topic_template: hunter/X-CORE/zone/{{ states('sensor.irrigatie_map_zone_achtertuin') }}
        payload_template: >
          { "action": "start", "time": {{ states('input_number.irrigatie_timer_' ~ states('sensor.irrigatie_map_zone_achtertuin') ~ '_achtertuin') | int }} }"

  irrigatie_uit_achtertuin:
    alias: Sproeier uit
    sequence:
    - service: mqtt.publish
      data_template:
        topic_template: hunter/X-CORE/zone/{{ states('input_text.irrigatie_actieve_zone_achtertuin') }}
        payload_template: >
          { "action": "stop" }

##################################
# Reset switch na aflopen timer  #
##################################
automation:
  - id: '1655392256832'
    alias: Irrigatie - reset switch wanneer timer klaar is
    description: ''
    trigger:
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.irrigatie_tijd_resterend_achtertuin
    condition: []
    action:
    - service: switch.turn_off
      data: {}
      target:
        entity_id: switch.irrigatie_achtertuin
    mode: single

Last week a user asked me about how I approached integrating the Hunter X-Core via DM. To help out future users getting started I will also post my answer to him in this topic.

Setup the mqtt broker

If you don’t want to communicate to the WeMos D1 Mini Pro via mqtt skip this step

First you setup a mqtt broker to communicate with the WeMos D1 Mini Pro (mosquitto).
I prefer communication via mqtt since you don’t need to know the WeMos IP address. And therefor don’t have to configure a static IP.

Setup the WeMos D1 Mini Pro

Install the software on the WeMos D1 Mini Pro as described on the Github page.

Make sure to fill in all requested information when you configure the WeMos via the web interface otherwise the WeMos won’t start properly and end up in a boot loop.
If you don’t want to use mqtt you have to fill in something in the web interface configuration otherwise you will end up in the boot loop.

Connect the Wemos to the Hunter X-Core

Now follow the guide on Github to connect the WeMos to The Hunter X-Core. The documentation linked to from the start post is also worth reading.

The WeMos fitted inside the Hunter’s case so the risk of someone accidently damaging it is pretty small.

Debugging mqtt

Use MQTT-Explorer to connect to your mosquito broker. If I remember correctly the WeMos should send a message to the broker on start up. That message will include the mqtt address. In my case this was hunter/X-CORE.

Integrating in Home Assistant

At this point you can copy past my code to a Home Assistant Package and modify to your preferences. As far as I understand a package is just a yaml file where you can bundle multiple entities with a certain purpose.

Add packages to your configuration.yaml (I added it at the top).

homeassistant:
  packages: !include_dir_named packages

Now add the configuration I posted in the topic to a package, in my case that is config/packages/irrigatie.yaml

I’ve set up the project as described (thank you dehaas). Wemos is connected to the 24VAC on the hunter using the 3.3V output from the board. I see the commands comming through in my mqtt broker and I’m getting a result back from the board.
So it all seems to work great except the fact that my hunter is doing nothing if I send a command.
Is there any way of checking if the Wemos is communicating correctly with my hunter?

Sorry for the noob question. But how can I get it on an ESP32 board? Is it possible to get me started with esptool? Thanks in advance!

@dehaas Your script package helped me a lot. I translated it to english and added some more zones. Here is my updated version

##################################
# Irrigate zone selectbox       #
##################################
input_select:
  irrigate_zone:
    name: Irrigate zone
    options:
      - zone1
      - zone2
      - zone2
      - zone3
      - zone4
      - zone5
      - zone6
      - zone7

template:
  - sensor:
    - name: irrigate_map_zone
      state: >
        {% set mapper =
          { 'zone1':'1',
            'zone2':'2',
            'zone3':'3',
            'zone4':'4',
            'zone5':'5',
            'zone6':'6',
            'zone7':'7'} %}
        {% set state = states('input_select.irrigate_zone') %}
        {% set id = mapper[state] if state in mapper %}
        {{ id }}

input_text:
  irrigation_active_zone:
    initial: inactive

##################################
# Irrigation zone time selection   #
##################################
input_number:
  irrigate_timer_1:
    name: Timer zone1
    initial: 60
    min: 1
    max: 120
    step: 1

  irrigate_timer_2:
    name: Timer zone2
    initial: 60
    min: 1
    max: 120
    step: 1

  irrigate_timer_3:
    name: Timer zone3
    initial: 60
    min: 1
    max: 120
    step: 1

  irrigate_timer_4:
    name: Timer zone4
    initial: 60
    min: 1
    max: 120
    step: 1

  irrigate_timer_5:
    name: Timer zone5
    initial: 60
    min: 1
    max: 120
    step: 1

  irrigate_timer_6:
    name: Timer zone6
    initial: 60
    min: 1
    max: 120
    step: 1

  irrigate_timer_7:
    name: Timer zone7
    initial: 60
    min: 1
    max: 120
    step: 1
################################################
# Selected Sprinkler switch script & timer #
################################################
timer:
  irrigation_time_remaining:
    name: Remaining Time
    duration: "00:00:00"

switch:
  - platform: template
    switches:
      irrigate_lawn:
        friendly_name: "Sprinkler On/Off"
        turn_on:
          - service: script.turn_on
            data:
              entity_id: script.irrigation_on
          - service: timer.start
            target:
              entity_id: timer.irrigation_time_remaining
            data:
              duration: "{{ states('input_number.irrigate_timer_' ~ states('sensor.irrigate_map_zone')) | int  * 60 }}"
          - service: input_text.set_value
            target:
              entity_id: input_text.irrigation_active_zone
            data:
              value: "{{ states('sensor.irrigate_map_zone') }}"
        turn_off:
          - service: script.turn_on
            data:
              entity_id: script.irrigation_off
          - service: timer.finish
            target:
              entity_id: timer.irrigation_time_remaining
          - service: input_text.set_value
            target:
              entity_id: input_text.irrigation_active_zone
            data:
              value: inactive
        icon_template: >-
               {% if is_state('switch.irrigate_lawn', 'on') %}
               mdi:water-pump
               {% else %}
               mdi:water-pump-off
               {% endif %}

##################################
# Invoke mqtt            #
##################################
script:
  irrigation_on:
    alias: Sprinkler On
    sequence:
    - service: mqtt.publish
      data_template:
        topic_template: hunter/X-CORE/zone/{{ states('sensor.irrigate_map_zone') }}
        payload_template: >
          { "action": "start", "time": {{ states('input_number.irrigate_timer_' ~ states('sensor.irrigate_map_zone')) | int }} }

  irrigation_off:
    alias: Sprinkler Off
    sequence:
    - service: mqtt.publish
      data_template:
        topic_template: hunter/X-CORE/zone/{{ states('input_text.irrigation_active_zone') }}
        payload_template: >
          { "action": "stop" }

##################################
# Reset switch when timer finishes #
##################################
automation:
  - id: '1655392256832'
    alias: Irrigatie - reset switch wanneer timer klaar is
    description: ''
    trigger:
    - platform: event
      event_type: timer.finished
      event_data:
        entity_id: timer.irrigation_time_remaining
    condition: []
    action:
    - service: switch.turn_off
      data: {}
      target:
        entity_id: switch.irrigate_lawn
    mode: single

I also made a similar UI card as yours. I added a conditonal card which will show corresponding zone time input. I tried template entity card, to conditionally show the corresponding zone time input, but it quite doesn’t do what I wanted :

type: entities
entities:
  - input_select.irrigate_zone
  - type: conditional
    conditions:
      - entity: sensor.irrigate_map_zone
        state: '1'
    row:
      entity: input_number.irrigate_timer_1
  - type: conditional
    conditions:
      - entity: sensor.irrigate_map_zone
        state: '2'
    row:
      entity: input_number.irrigate_timer_2
  - type: conditional
    conditions:
      - entity: sensor.irrigate_map_zone
        state: '3'
    row:
      entity: input_number.irrigate_timer_3
  - type: conditional
    conditions:
      - entity: sensor.irrigate_map_zone
        state: '4'
    row:
      entity: input_number.irrigate_timer_4
  - type: conditional
    conditions:
      - entity: sensor.irrigate_map_zone
        state: '5'
    row:
      entity: input_number.irrigate_timer_5
  - type: conditional
    conditions:
      - entity: sensor.irrigate_map_zone
        state: '6'
    row:
      entity: input_number.irrigate_timer_6
  - type: conditional
    conditions:
      - entity: sensor.irrigate_map_zone
        state: '7'
    row:
      entity: input_number.irrigate_timer_7
  - type: custom:template-entity-row
    entity: input_number.irrigate_timer_{{ states('sensor.irrigate_map_zone')}}
  - entity: switch.irrigate_lawn
  - entity: timer.irrigation_time_remaining
  - input_text.irrigation_active_zone