2025 - Recteq Smoker Home Assistant via TuyaLocal

OK folks, I just got this working. It’s 2025 now and a lot of the old information and integrations are no longer working, furthermore the default HACS TuyaLocal does not work with my smoker (it’s read only, can’t even turn it on or set the temperature). So for my own benefit (in case I need to do it again or remember what I did) and for others that run into the same issues, I’m going to try to document everything I did. I pulled a lot of inspiration directly from this post. and there’s a lot of information about IDs for different smokers on this GitHub post.

I have a RT-700 (Bull) so all of the information here is specific to the RT-700. Refer to the earlier post for info if you have a different smoker.

I attempted to get this working some time ago, so I already had a Tuya developer account. There are walkthroughs on getting that set up and others that show you how to use a rooted android phone to get your local device keys. I’m going to assume you already have an account and have failed to get the integration working (like me!) and just need the push in the right direction.

So You go to Tuya Smart Developer Center and log in, if you haven’t already. Then open the project. First we need to grab your ClientID and Client Secret


And your UserID

I would recommend you put these somewhere for future reference like 1Password or similar.

OK, now it’s time to install TuyaLocal - BUT!!! Don’t install the one that’s already in HACS - that one doesn’t work. You need to install this integration instead: https://github.com/xZetsubou/hass-localtuya. I liked using the install links in the documentation, made it a breeze!

OK, so you have it installed - time to configure it. Adding the hub using your saved credentials is easy enough, where I ran into problems was when I went to actually configure the smoker. The DPS were not being recognized, I had to put commas in between them: 1, 102, 103, 105, 106. I also found that protocol 3.1 works and 3.3 did not for my particular smoker, so be ready to play around with the protocol too if you have issues.

I had to do this a few times so here is my cheat sheet of IDs and entity names. If you’re “leveraging” my setup, you may want the same entity IDs:

102
Smoker
climate.smoker_set_temp

1
Smoker
switch.smoker_on_off

103
Smoker Actual Temperature
sensor.actual_temperature
°F -- you're going to be pasting this a LOT and °F is not the same as F

105
Smoker Probe A Temperature
sensor.probe_a

106
Smoker Probe B Temperature
sensor.probe_b

You need to configure the temperature control FIRST. This will let you use the DPs as part of the control. In my case, 102 is the target, 103 is current, and 1 turns the smoker on and off. Here are all the settings I changed



FUN FACT! If you do not set the “(Optional) Temperature Unit” to °F - it’s going to assume the numbers are Celsius and convert it into Fahrenheit and suddenly it shows your grill set to 600°F - ask me how I know.

Alright, now let’s go back and set up the smoker on/off button (1)

Now we want a sensor for the current temp (103)

Let’s do Probe A (105)

And Probe B (106)

At least with the RT-700 Bull, you can totally stop here, or if you like, you can configure the other entries. I’m not currently using them on anything but it’s up to you.





Cool. Next it’s time to create a bunch of helpers and then create a dashboard.

On this post they used a Lovelace (dashboard) thermostat card called simple-thermostat for the dashboard. I think it works well so let’s install it using HACS

Now let’s make some helpers. http://homeassistant.local:8123/config/helpers Note the type of helper is shown in the EntityID

Name: Smoker Target Temperature
Icon mdi:thermometer
Unit of Measurement: °F
Display Precision: 0
EntityID: sensor.smoker_target_temperature
Template: {{ states('number.set_temp') | round(0, default=0) }}
Unit of Measurement: °F
Device Class: temperature
State Class: Measurement


Name: Smoker Probe-A Target
Icon mdi:thermometer
EntityID: input_number.smoker_probe_a_target
Min Value: 0
Max value: 250
Step size: 5
Unit of Measurement: °F


Name: Smoker Probe-B Target
Icon mdi:thermometer
EntityID: input_number.smoker_probe_b_target
Min Value: 0
Max value: 250
Step size: 5
Unit of Measurement: °F


Name: Smoker Probe-A Status
Icon mdi:food-steak
EntityID: sensor.smoker_probe_a_status
Template: 
{% if states('sensor.smoker_probe_a_temperature') != unavailable %}
          {% set target = states('input_number.smoker_probe_a_target')|int %}
          {% set actual = states('sensor.probe_a')|int %}
          {% set offset = actual - target %}
          {% if offset > 5 %}Over Temp!
          {% elif offset > -5 %}At Target
          {% elif offset > -15 %}Approaching...
          {% else %}Not Cooked Enough{% endif %}
{%- endif %}


Name: Smoker Probe-B Status
Icon mdi:food-steak
EntityID: sensor.smoker_probe_b_status
Template: 
{% if states('sensor.smoker_probe_b_temperature') != unavailable %}
          {% set target = states('input_number.smoker_probe_b_target')|int %}
          {% set actual = states('sensor.probe_b')|int %}
          {% set offset = actual - target %}
          {% if offset > 5 %}Over Temp!
          {% elif offset > -5 %}At Target
          {% elif offset > -15 %}Approaching...
          {% else %}Not Cooked Enough{% endif %}
{%- endif %}


Name: Probe-A Percent
Icon mdi:food-steak
Display Precision: 0
EntityID: sensor.probe_a_percent
Template: 
{% if states('sensor.smoker_probe_a_temperature') != unavailable %}
          {% set target = states('input_number.smoker_probe_a_target')|float %}
          {% set actual = states('sensor.probe_a')|float %}
          {% set percent = (actual / target) * 100 %}
          {{percent}}
{%- endif %}
Unit of Measurement: %
State Class: Measurement


Name: Probe-B Percent
Icon mdi:food-steak
Display Precision: 0
EntityID: sensor.probe_b_percent
Template: 
{% if states('sensor.smoker_probe_b_temperature') != unavailable %}
          {% set target = states('input_number.smoker_probe_b_target')|float %}
          {% set actual = states('sensor.probe_b')|float %}
          {% set percent = (actual / target) * 100 %}
          {{percent}}
{%- endif %}
Unit of Measurement: %
State Class: Measurement

We need to set some limits to the climate controller. Apparently this has to be done in the raw config yaml files. I happen to have Studio Code Server installed which makes that super easy to do.

Lovelace Thermostat Card Fix

This is required because the LocalTuya configuration for the entity will not take free-text values and none of the DPS parameters from the smoker itself provide the max/min. We are going to hard-code these. I assume you’ve never configured a customization before below. If you have, you know where to start :slight_smile:

  1. Create a new customize.yaml file in the root of the config folder. I use the Visual Code Studio host add-on so this is very easy from within HA.
  2. Include the following in the new customize.yaml file, replacing the climate.smoker_temperature with the appropriate entity ID:
climate.smoker:
  max_temp: 500
  min_temp: 180
  1. Save the file.
  2. Open the configuration.yaml file and locate a line that states homeassistant:; if this does not exist, add it on a new line at the bottom.
  3. Indent two spaces, and add customize: !include customize.yaml and save the file. The added text should be:
homeassistant:
  customize: !include customize.yaml

Restart HA

OK, that was a lot. Let’s make a dashboard. I used a lot of the details (again) from this post

But the setup is different in Home Assistant now and I couldn’t just copy/paste it, so I got to do “New dashboard from scratch.” Once that is made, you can choose to either use the following information to make the dashboard using the GUI (like I did), or you can try pasting it into the “{}raw configuration editor” and see if it works:

views:
  - title: Smoker
    sections:
      - type: grid
        cards:
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'off'
            card:
              type: glance
              entities:
                - entity: switch.smoker_on_off
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
            card:
              type: custom:simple-thermostat
              entity: climate.smoker_set_temp
              decimals: 0
              step_size: 5
              hide:
                state: true
              header:
                toggle:
                  entity: switch.smoker_on_off
              layout:
                step: row
                mode:
                  headings: false
                  icons: false
                  names: false
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
              - condition: numeric_state
                entity: sensor.probe_a
                above: 0
              - condition: numeric_state
                entity: sensor.probe_b
                below: 1
            card:
              type: history-graph
              entities:
                - entity: sensor.probe_a
                  name: Probe-A
                - entity: sensor.smoker_target_temperature
                  name: Target
                - entity: sensor.actual_temperature
                  name: Actual
              hours_to_show: 8
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
              - condition: numeric_state
                entity: sensor.probe_a
                above: 1
              - condition: numeric_state
                entity: sensor.probe_b
                below: 0
            card:
              type: history-graph
              entities:
                - entity: sensor.probe_b
                  name: Probe-B
                - entity: sensor.smoker_target_temperature
                  name: Target
                - entity: sensor.actual_temperature
                  name: Actual
              hours_to_show: 8
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
              - entity: sensor.probe_a
                state: unavailable
              - entity: sensor.probe_b
                state: unavailable
            card:
              type: history-graph
              entities:
                - entity: sensor.smoker_target_temperature
                  name: Target
                - entity: sensor.actual_temperature
                  name: Actual
              hours_to_show: 8
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
              - condition: numeric_state
                entity: sensor.probe_a
                above: 0
              - condition: numeric_state
                entity: sensor.probe_b
                above: 0
            card:
              type: history-graph
              entities:
                - entity: sensor.probe_a
                  name: Probe-A
                - entity: sensor.probe_b
                  name: Probe-B
                - entity: sensor.smoker_target_temperature
                  name: Target
                - entity: sensor.actual_temperature
                  name: Actual
              hours_to_show: 8
      - type: grid
        cards:
          - type: picture
            image: >-
              https://support.recteq.com/hc/theming_assets/01HZPQW9TJCNCSY7DCNFCGC2BX
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
              - condition: numeric_state
                entity: sensor.probe_a
                above: 0
            card:
              type: glance
              entities:
                - entity: sensor.probe_a
                  name: Probe-A
                - entity: input_number.smoker_probe_a_target
                  name: Target
                - entity: sensor.smoker_probe_a_status
                  name: Status
                - entity: sensor.probe_a_percent
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
              - condition: numeric_state
                entity: sensor.probe_a
                above: 0
            card:
              type: gauge
              entity: sensor.probe_a_percent
              severity:
                green: 60
                yellow: 80
                red: 90
              needle: true
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
              - condition: numeric_state
                entity: sensor.probe_b
                above: 0
            card:
              type: glance
              entities:
                - entity: sensor.probe_b
                  name: Probe-B
                - entity: input_number.smoker_probe_b_target
                  name: Target
                - entity: sensor.smoker_probe_b_status
                  name: Status
                - entity: sensor.probe_b_percent
          - type: conditional
            conditions:
              - condition: state
                entity: switch.smoker_on_off
                state: 'on'
              - condition: numeric_state
                entity: sensor.probe_b
                above: 0
            card:
              type: gauge
              entity: sensor.probe_b_percent
              severity:
                green: 60
                yellow: 80
                red: 90
              needle: true
    type: sections
    max_columns: 2
    icon: mdi:grill
    cards: []

You’ll notice that a lot of the entries show and hide based on what the smoker is doing. If the smoker is off, it only shows the main switch, then it shows different graphs and such depending on if the probe is plugged in or not.

So… that’s it so far as I can remember =)

1 Like

So when setting this up, where do you get the local key value?

Sorry, just saw this today. The local key is auto populated when you set up the developer account and then use the alternative Tuya Local referenced in the first post