Moes BHT-002 Thermostat local control (Tuya based)

hy did you fiind the setings (problem is there no status if idle or heating).Thank you!

Add this to your climate file, replace existing part with this.

HVAC_MODE_SETS = {
ā€œmanual/autoā€: {
HVAC_MODE_HEAT: ā€œmanualā€,
HVAC_MODE_AUTO: ā€œautoā€,
},
ā€œManual/Autoā€: {
HVAC_MODE_HEAT: ā€œManualā€,
HVAC_MODE_AUTO: ā€œAutoā€,
},
ā€œManual/Programā€: {
HVAC_MODE_HEAT: ā€œManualā€,
HVAC_MODE_AUTO: ā€œProgramā€,
},
ā€œTrue/Falseā€: {
HVAC_MODE_HEAT: True,
},
ā€œ1/0ā€: {
HVAC_MODE_HEAT: ā€œ1ā€,
HVAC_MODE_AUTO: ā€œ0ā€,
},
}

1 Like

It didnā€™t work for me. Changed my Climate Entity to Unavailable.

Anybody has any idea on how to make the fire icon turn on if the target temperature is higher than the actual, and off otherwise?

Are you sure you edited it correctly? Also did you do a restart after the changes? Mine work perfect all 3 of them

So once again, go to climate file finder section HVAV_MODE_SETS change that text with the above. Restart HA, add thermostat and all should work fine power icon to turn off, flame to turn on and calendar to set to program mode.

Yes! You are right. It works now. Turns out that copying and pasting your code left me with the wrong quotes on the yaml file.
May I suggest you edit HVAC_MODE_SETS and put it in code mode?

Again, thanks for the quick reply.

I think now there are 2 things missing on this integration, that the tuya_local already has:

  1. The Comfort / ECO that shows the mode on the screen.
    Screenshot from 2022-08-13 19-43-55

  2. The ability to Lock
    Screenshot from 2022-08-13 19-44-09

Both things are available for the same hardware on the Tuya_Local integration but not available on the LocalTuya integration (which is the one we are talking about here)

I canā€™t see nothing in your screenshot.

Canā€™t get the auto mode to be overriden by the manual mode for sayā€¦ an hour, and then go back to auto modeā€¦

Hey Net786,
Iā€™m running HA on a rasp (Hassio) and have this issue with auto showing up instead of the manual setting.
I tried to follow your advice to add the lines in the climate file, but Iā€™m not sure to understand where to find this file.
I integrated the Thermostat by LocalTuya integration.

Could you please help me out?

Thanks!

Hey,

It is under custom components , local tuya, climate

Thank you!

I have this in my climate file. Where should I add what you pasted (that I see in my config already by the way)?:

"""Platform to locally control Tuya-based climate devices."""
import asyncio
import logging
from functools import partial

import voluptuous as vol
from homeassistant.components.climate import (
    DEFAULT_MAX_TEMP,
    DEFAULT_MIN_TEMP,
    DOMAIN,
    ClimateEntity,
)
from homeassistant.components.climate.const import (
    CURRENT_HVAC_HEAT,
    CURRENT_HVAC_IDLE,
    HVAC_MODE_AUTO,
    HVAC_MODE_HEAT,
    HVAC_MODE_OFF,
    PRESET_AWAY,
    PRESET_ECO,
    PRESET_HOME,
    PRESET_NONE,
    SUPPORT_PRESET_MODE,
    SUPPORT_TARGET_TEMPERATURE,
    SUPPORT_TARGET_TEMPERATURE_RANGE,
)
from homeassistant.const import (
    ATTR_TEMPERATURE,
    CONF_TEMPERATURE_UNIT,
    PRECISION_HALVES,
    PRECISION_TENTHS,
    PRECISION_WHOLE,
    TEMP_CELSIUS,
    TEMP_FAHRENHEIT,
)

from .common import LocalTuyaEntity, async_setup_entry
from .const import (
    CONF_CURRENT_TEMPERATURE_DP,
    CONF_ECO_DP,
    CONF_ECO_VALUE,
    CONF_HEURISTIC_ACTION,
    CONF_HVAC_ACTION_DP,
    CONF_HVAC_ACTION_SET,
    CONF_HVAC_MODE_DP,
    CONF_HVAC_MODE_SET,
    CONF_MAX_TEMP_DP,
    CONF_MIN_TEMP_DP,
    CONF_PRECISION,
    CONF_PRESET_DP,
    CONF_PRESET_SET,
    CONF_TARGET_PRECISION,
    CONF_TARGET_TEMPERATURE_DP,
    CONF_TEMPERATURE_STEP,
)

_LOGGER = logging.getLogger(__name__)

HVAC_MODE_SETS = {
    "manual/auto": {
        HVAC_MODE_HEAT: "manual",
        HVAC_MODE_AUTO: "auto",
    },
    "Manual/Auto": {
        HVAC_MODE_HEAT: "Manual",
        HVAC_MODE_AUTO: "Auto",
    },
    "Manual/Program": {
        HVAC_MODE_HEAT: "Manual",
        HVAC_MODE_AUTO: "Program",
    },
    "True/False": {
        HVAC_MODE_HEAT: True,
    },
    "1/0": {
        HVAC_MODE_HEAT: "1",
        HVAC_MODE_AUTO: "0",
    },
}
HVAC_ACTION_SETS = {
    "True/False": {
        CURRENT_HVAC_HEAT: True,
        CURRENT_HVAC_IDLE: False,
    },
    "open/close": {
        CURRENT_HVAC_HEAT: "open",
        CURRENT_HVAC_IDLE: "close",
    },
    "heating/no_heating": {
        CURRENT_HVAC_HEAT: "heating",
        CURRENT_HVAC_IDLE: "no_heating",
    },
    "Heat/Warming": {
        CURRENT_HVAC_HEAT: "Heat",
        CURRENT_HVAC_IDLE: "Warming",
    },
}
PRESET_SETS = {
    "Manual/Holiday/Program": {
        PRESET_AWAY: "Holiday",
        PRESET_HOME: "Program",
        PRESET_NONE: "Manual",
    },
}

TEMPERATURE_CELSIUS = "celsius"
TEMPERATURE_FAHRENHEIT = "fahrenheit"
DEFAULT_TEMPERATURE_UNIT = TEMPERATURE_CELSIUS
DEFAULT_PRECISION = PRECISION_TENTHS
DEFAULT_TEMPERATURE_STEP = PRECISION_HALVES
# Empirically tested to work for AVATTO thermostat
MODE_WAIT = 0.1


def flow_schema(dps):
    """Return schema used in config flow."""
    return {
        vol.Optional(CONF_TARGET_TEMPERATURE_DP): vol.In(dps),
        vol.Optional(CONF_CURRENT_TEMPERATURE_DP): vol.In(dps),
        vol.Optional(CONF_TEMPERATURE_STEP): vol.In(
            [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS]
        ),
        vol.Optional(CONF_MAX_TEMP_DP): vol.In(dps),
        vol.Optional(CONF_MIN_TEMP_DP): vol.In(dps),
        vol.Optional(CONF_PRECISION): vol.In(
            [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS]
        ),
        vol.Optional(CONF_HVAC_MODE_DP): vol.In(dps),
        vol.Optional(CONF_HVAC_MODE_SET): vol.In(list(HVAC_MODE_SETS.keys())),
        vol.Optional(CONF_HVAC_ACTION_DP): vol.In(dps),
        vol.Optional(CONF_HVAC_ACTION_SET): vol.In(list(HVAC_ACTION_SETS.keys())),
        vol.Optional(CONF_ECO_DP): vol.In(dps),
        vol.Optional(CONF_ECO_VALUE): str,
        vol.Optional(CONF_PRESET_DP): vol.In(dps),
        vol.Optional(CONF_PRESET_SET): vol.In(list(PRESET_SETS.keys())),
        vol.Optional(CONF_TEMPERATURE_UNIT): vol.In(
            [TEMPERATURE_CELSIUS, TEMPERATURE_FAHRENHEIT]
        ),
        vol.Optional(CONF_TARGET_PRECISION): vol.In(
            [PRECISION_WHOLE, PRECISION_HALVES, PRECISION_TENTHS]
        ),
        vol.Optional(CONF_HEURISTIC_ACTION): bool,
    }


class LocaltuyaClimate(LocalTuyaEntity, ClimateEntity):
    """Tuya climate device."""

    def __init__(
        self,
        device,
        config_entry,
        switchid,
        **kwargs,
    ):
        """Initialize a new LocaltuyaClimate."""
        super().__init__(device, config_entry, switchid, _LOGGER, **kwargs)
        self._state = None
        self._target_temperature = None
        self._current_temperature = None
        self._hvac_mode = None
        self._preset_mode = None
        self._hvac_action = None
        self._precision = self._config.get(CONF_PRECISION, DEFAULT_PRECISION)
        self._target_precision = self._config.get(
            CONF_TARGET_PRECISION, self._precision
        )
        self._conf_hvac_mode_dp = self._config.get(CONF_HVAC_MODE_DP)
        self._conf_hvac_mode_set = HVAC_MODE_SETS.get(
            self._config.get(CONF_HVAC_MODE_SET), {}
        )
        self._conf_preset_dp = self._config.get(CONF_PRESET_DP)
        self._conf_preset_set = PRESET_SETS.get(self._config.get(CONF_PRESET_SET), {})
        self._conf_hvac_action_dp = self._config.get(CONF_HVAC_ACTION_DP)
        self._conf_hvac_action_set = HVAC_ACTION_SETS.get(
            self._config.get(CONF_HVAC_ACTION_SET), {}
        )
        self._conf_eco_dp = self._config.get(CONF_ECO_DP)
        self._conf_eco_value = self._config.get(CONF_ECO_VALUE, "ECO")
        self._has_presets = self.has_config(CONF_ECO_DP) or self.has_config(
            CONF_PRESET_DP
        )
        _LOGGER.debug("Initialized climate [%s]", self.name)

    @property
    def supported_features(self):
        """Flag supported features."""
        supported_features = 0
        if self.has_config(CONF_TARGET_TEMPERATURE_DP):
            supported_features = supported_features | SUPPORT_TARGET_TEMPERATURE
        if self.has_config(CONF_MAX_TEMP_DP):
            supported_features = supported_features | SUPPORT_TARGET_TEMPERATURE_RANGE
        if self.has_config(CONF_PRESET_DP) or self.has_config(CONF_ECO_DP):
            supported_features = supported_features | SUPPORT_PRESET_MODE
        return supported_features

    @property
    def precision(self):
        """Return the precision of the system."""
        return self._precision

    @property
    def target_precision(self):
        """Return the precision of the target."""
        return self._target_precision

    @property
    def temperature_unit(self):
        """Return the unit of measurement used by the platform."""
        if (
            self._config.get(CONF_TEMPERATURE_UNIT, DEFAULT_TEMPERATURE_UNIT)
            == TEMPERATURE_FAHRENHEIT
        ):
            return TEMP_FAHRENHEIT
        return TEMP_CELSIUS

    @property
    def hvac_mode(self):
        """Return current operation ie. heat, cool, idle."""
        return self._hvac_mode

    @property
    def hvac_modes(self):
        """Return the list of available operation modes."""
        if not self.has_config(CONF_HVAC_MODE_DP):
            return None
        return list(self._conf_hvac_mode_set) + [HVAC_MODE_OFF]

    @property
    def hvac_action(self):
        """Return the current running hvac operation if supported.

        Need to be one of CURRENT_HVAC_*.
        """
        if self._config.get(CONF_HEURISTIC_ACTION, False):
            if self._hvac_mode == HVAC_MODE_HEAT:
                if self._current_temperature < (
                    self._target_temperature - self._precision
                ):
                    self._hvac_action = CURRENT_HVAC_HEAT
                if self._current_temperature == (
                    self._target_temperature - self._precision
                ):
                    if self._hvac_action == CURRENT_HVAC_HEAT:
                        self._hvac_action = CURRENT_HVAC_HEAT
                    if self._hvac_action == CURRENT_HVAC_IDLE:
                        self._hvac_action = CURRENT_HVAC_IDLE
                if (
                    self._current_temperature + self._precision
                ) > self._target_temperature:
                    self._hvac_action = CURRENT_HVAC_IDLE
            return self._hvac_action
        return self._hvac_action

    @property
    def preset_mode(self):
        """Return current preset."""
        return self._preset_mode

    @property
    def preset_modes(self):
        """Return the list of available presets modes."""
        if not self._has_presets:
            return None
        presets = list(self._conf_preset_set)
        if self._conf_eco_dp:
            presets.append(PRESET_ECO)
        return presets

    @property
    def current_temperature(self):
        """Return the current temperature."""
        return self._current_temperature

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        return self._target_temperature

    @property
    def target_temperature_step(self):
        """Return the supported step of target temperature."""
        return self._config.get(CONF_TEMPERATURE_STEP, DEFAULT_TEMPERATURE_STEP)

    @property
    def fan_mode(self):
        """Return the fan setting."""
        return NotImplementedError()

    @property
    def fan_modes(self):
        """Return the list of available fan modes."""
        return NotImplementedError()

    async def async_set_temperature(self, **kwargs):
        """Set new target temperature."""
        if ATTR_TEMPERATURE in kwargs and self.has_config(CONF_TARGET_TEMPERATURE_DP):
            temperature = round(kwargs[ATTR_TEMPERATURE] / self._target_precision)
            await self._device.set_dp(
                temperature, self._config[CONF_TARGET_TEMPERATURE_DP]
            )

    def set_fan_mode(self, fan_mode):
        """Set new target fan mode."""
        return NotImplementedError()

    async def async_set_hvac_mode(self, hvac_mode):
        """Set new target operation mode."""
        if hvac_mode == HVAC_MODE_OFF:
            await self._device.set_dp(False, self._dp_id)
            return
        if not self._state and self._conf_hvac_mode_dp != self._dp_id:
            await self._device.set_dp(True, self._dp_id)
            # Some thermostats need a small wait before sending another update
            await asyncio.sleep(MODE_WAIT)
        await self._device.set_dp(
            self._conf_hvac_mode_set[hvac_mode], self._conf_hvac_mode_dp
        )

    async def async_turn_on(self) -> None:
        """Turn the entity on."""
        await self._device.set_dp(True, self._dp_id)

    async def async_turn_off(self) -> None:
        """Turn the entity off."""
        await self._device.set_dp(False, self._dp_id)

    async def async_set_preset_mode(self, preset_mode):
        """Set new target preset mode."""
        if preset_mode == PRESET_ECO:
            await self._device.set_dp(self._conf_eco_value, self._conf_eco_dp)
            return
        await self._device.set_dp(
            self._conf_preset_set[preset_mode], self._conf_preset_dp
        )

    @property
    def min_temp(self):
        """Return the minimum temperature."""
        if self.has_config(CONF_MIN_TEMP_DP):
            return self.dps_conf(CONF_MIN_TEMP_DP)
        return DEFAULT_MIN_TEMP

    @property
    def max_temp(self):
        """Return the maximum temperature."""
        if self.has_config(CONF_MAX_TEMP_DP):
            return self.dps_conf(CONF_MAX_TEMP_DP)
        return DEFAULT_MAX_TEMP

    def status_updated(self):
        """Device status was updated."""
        self._state = self.dps(self._dp_id)

        if self.has_config(CONF_TARGET_TEMPERATURE_DP):
            self._target_temperature = (
                self.dps_conf(CONF_TARGET_TEMPERATURE_DP) * self._target_precision
            )

        if self.has_config(CONF_CURRENT_TEMPERATURE_DP):
            self._current_temperature = (
                self.dps_conf(CONF_CURRENT_TEMPERATURE_DP) * self._precision
            )

        if self._has_presets:
            if (
                self.has_config(CONF_ECO_DP)
                and self.dps_conf(CONF_ECO_DP) == self._conf_eco_value
            ):
                self._preset_mode = PRESET_ECO
            else:
                for preset, value in self._conf_preset_set.items():  # todo remove
                    if self.dps_conf(CONF_PRESET_DP) == value:
                        self._preset_mode = preset
                        break
                else:
                    self._preset_mode = PRESET_NONE

        # Update the HVAC status
        if self.has_config(CONF_HVAC_MODE_DP):
            if not self._state:
                self._hvac_mode = HVAC_MODE_OFF
            else:
                for mode, value in self._conf_hvac_mode_set.items():
                    if self.dps_conf(CONF_HVAC_MODE_DP) == value:
                        self._hvac_mode = mode
                        break
                else:
                    # in case hvac mode and preset share the same dp
                    self._hvac_mode = HVAC_MODE_AUTO

        # Update the current action
        for action, value in self._conf_hvac_action_set.items():
            if self.dps_conf(CONF_HVAC_ACTION_DP) == value:
                self._hvac_action = action


async_setup_entry = partial(async_setup_entry, DOMAIN, LocaltuyaClimate, flow_schema)

Any idea on this @ned786 ?
Thanks!

EDIT:

@ned786
It is 4.1.1 by rospogrigio.

Thanks

Hey, what version of local Tuya are you using?

Itā€™s 4.1.1 by rospogrigio

I am using a BHT-002 thermostat under HA 2023.2.3 using LocalTuya v5.0.0.

Is there any way to capture the hvac_action attribute, i.e. the actual state of the thermostat (whether heating is active or not, that is) and not just set the mode to auto/manual and controlling the temperature?

I found a list of DPs in a github bug issue (Climate platform Ā· Issue #439 Ā· rospogrigio/localtuya Ā· GitHub) where this action is not defined/included.

Any ideas whether the DP which shows the hvac_action status is available?

Hi guys
iā€™m trying to configure my BHT-002 wireless with localtuya, but even if i complete the configuration, i get an ā€œunavailableā€ climate entity.

Can anyone share a screen of the conf of a working BHT-002 with localtuya?

Hi mate,

This is my first post here, just arrived trying to get some help on my mistaken order of 5 bht -002 GALWā€¦.

My heating system is based in water flooring but connected to a water boiler, then when I did the order I choose GA type but after receiving and installed today the water boiler is always working even when the thermostats are not requesting any kind of operation to the water boiler.

I need to set up the thermostats wit dry contact

I have sent a message to the AliExpress seller just to know if I can return the goods and receive the right ones but as today is Friday and they said they are not working on weekends, I was looking for an alternative solution.

Is there any other option to convert GA into GC type?

I saw your post and if Iā€™m not wrong youā€™re using a GA but the correct one for you is GC. Then youā€™re not connecting the two wires from the water boiler but only one in the number 2 relay. Is that correct? From my absolute unknown let me ask you if this would cause any kind of damage to the water heating flooring system or the water boiler.

This is my first attempt of automation and my Intention is to use it with smart life (tuya app) for the moment.

Thanks!!

Br
Andova


Hey Kendryn,
This is how my thermostat is set up.
Also, I had a lot of problems after setting the ECO presetā€¦ so Iā€™m not using it anymore and donā€™t recommend doing so. Itā€™s rather useless anyway since you can change the target temp whichever way you want with HA.
Be sure NOT to use ANY Scan Interval on the previous ā€œscreenā€ or you will have lots of problems with ā€œunavailable deviceā€. If you have set it by mistakeā€¦ I think the only way to remove it is to edit out of your ā€œconfig/.storage/core.config_entriesā€ file without having to remove the whole integration. Trying to edit the entity via the integrationā€™s dashboard didnā€™t remove this entry from my config, I had to remove it manually.

Cheers

2 Likes

Hi, I have these thermostats at home and I have one question. Is it possible to get information from the current temperature sensor in HA? I couldnā€™t find this entity anywhere.