rospogrigio / localtuya

local handling for Tuya devices
GNU General Public License v3.0
2.71k stars 530 forks source link

Climate addons #634

Open caramot opened 2 years ago

caramot commented 2 years ago

Hello,

please see below the code to add new addins climate on tuyalocal

on init.py file: line 31

      - platform: climate
        friendly_name: Device climate
        id: 1  #on/off id [True or False] 
        heat_cold: 10 # [True or False] param heating or air conditioning
        temperature_target: 16  # Optional Target of temperature
        min_temp_id: 26  # Optional min temp id 
        max_temp_id: 19  # Optional max temp id
        current_temperature: 24  #  Optional Sensor of temperature value ex : 201 for me 20.1°C
        auxillary_heat: 11   #  Optional auxillary heat [True or False] 
        humidity_target: 20  # Optional Target of humidity 
        current_humidity: 21  # Optional Sensor of humidity

on new file climate.py

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

import voluptuous as vol
from homeassistant.components.climate import (
    DOMAIN,
    ATTR_TARGET_TEMP_HIGH,
    ATTR_TARGET_TEMP_LOW,
    ATTR_HVAC_MODE,
    DEFAULT_MIN_TEMP, 
    TEMP_CELSIUS,
    HVAC_MODES,
    SUPPORT_TARGET_TEMPERATURE,
    SUPPORT_AUX_HEAT,
    SUPPORT_TARGET_HUMIDITY,
    ClimateEntity
)

from .common import LocalTuyaEntity, async_setup_entry
from .const import (
    CONF_CLIMATE_TEMPERATURE,
    CONF_CLIMATE_CURRENT,
    CONF_CLIMATE_HEAT_COLD,
    CONF_CLIMATE_TEMPERATURE_HIGH,
    CONF_CLIMATE_TEMPERATURE_LOW,
    CONF_CLIMATE_AUX_HEAT,
    CONF_CLIMATE_HUMIDITY,
    CONF_CLIMATE_CURRENT_HUMIDITY
 )

_LOGGER = logging.getLogger(__name__)

def flow_schema(dps):
    """Return schema used in config flow."""
    return {
        vol.Optional(CONF_CLIMATE_HEAT_COLD): vol.In(dps),
        vol.Optional(CONF_CLIMATE_CURRENT): vol.In(dps),
        vol.Optional(CONF_CLIMATE_TEMPERATURE): vol.In(dps),
        vol.Optional(CONF_CLIMATE_TEMPERATURE_HIGH): vol.In(dps),
        vol.Optional(CONF_CLIMATE_TEMPERATURE_LOW): vol.In(dps),
        vol.Optional(CONF_CLIMATE_AUX_HEAT): vol.In(dps),
        vol.Optional(CONF_CLIMATE_HUMIDITY): vol.In(dps),
        vol.Optional(CONF_CLIMATE_CURRENT_HUMIDITY): vol.In(dps)
    }

class LocaltuyaClimate(LocalTuyaEntity, ClimateEntity):
    """Representation of a Tuya climate."""

    def __init__(
        self,
        device,
        config_entry,
        climateid,
        **kwargs,
    ):
        """Initialize the entity."""
        super().__init__(device, config_entry, climateid, _LOGGER, **kwargs)
        self._state = False
        self._target_temperature = DEFAULT_MIN_TEMP 
        self._temperature_unit = TEMP_CELSIUS
        self._current_temperature = None
        self._hvac_mode = HVAC_MODES[1]
        self._hvac_modes = [HVAC_MODES[0],HVAC_MODES[1],HVAC_MODES[2]]
        self._min_temp = None
        self._max_temp = None
        self._target_temperature_step = 1
        self._precision = 0.2
        self._is_aux_heat = None
        self._current_humidity = None
        self._target_humidity = None

    @property
    def state(self):
        """Check if Tuya climate state."""
        return self._hvac_mode

    @property
    def temperature_unit(self) -> str:
        """Return the current temperature_unit."""
        return self._temperature_unit

    @property
    def precision(self) -> float:
        """Return the current precision."""
        return self._precision

    @property
    def hvac_mode(self) -> str:
        """Return the current precision."""
        return self._hvac_mode

    @property
    def hvac_modes(self):
        """Return the current precision."""
        return self._hvac_modes

    @property
    def target_temperature_step(self) -> float:
        """Return the current target step."""
        return self._target_temperature_step

    @property
    def target_temperature(self) -> float:
        """Return the current target temp."""
        return self._target_temperature

    @property
    def max_temp(self) -> float:
        """Return the current target temp high."""
        return self._max_temp

    @property
    def min_temp(self) -> float:
        """Return the current target temp low."""
        return self._min_temp

    @property
    def current_temperature(self) -> float:
        """Return the current target temp."""
        return self._current_temperature

    @property
    def is_aux_heat(self) -> bool :
        """Return the current status aux heat."""
        return self._is_aux_heat

    @property
    def current_humidity(self) -> int:
        """Return the current humidity."""
        return self._current_humidity

    @property
    def target_humidity(self) -> int:
        """Return the humidity we try to reach."""
        return self._target_humidity

    async def async_set_hvac_mode(self, hvac_mode: str, **kwargs) -> None:
       """Turn on the entity."""
       if hvac_mode==HVAC_MODES[1] or hvac_mode==HVAC_MODES[2]  :  #heat #cold
           await self._device.set_dp(hvac_mode==HVAC_MODES[2], self._config.get(CONF_CLIMATE_HEAT_COLD))
           if not self.dps(self._dp_id):
              time.sleep(0.6)
              await self._device.set_dp(True, self._dp_id)  
       else:
           await self._device.set_dp(False, self._dp_id)
       self.schedule_update_ha_state()

    async def async_set_temperature(self, temperature: float, **kwargs) -> None:
        """Set the temperature of the climate."""
        await self._device.set_dp(round(temperature) , self._config.get(CONF_CLIMATE_TEMPERATURE))
        self.schedule_update_ha_state()

    async def async_turn_aux_heat_off(self, **kwargs) -> None:
        """Set off aux heat of the climate."""
        await self._device.set_dp(False , self._config.get(CONF_CLIMATE_AUX_HEAT))
        self.schedule_update_ha_state()

    async def async_turn_aux_heat_on(self, **kwargs) -> None:
        """Set on aux heat of the climate."""
        await self._device.set_dp(True , self._config.get(CONF_CLIMATE_AUX_HEAT))
        self.schedule_update_ha_state()

    async def async_set_humidity(self, humidity: int) -> None: 
        """Set humidity of the climate."""
        await self._device.set_dp(round(humidity), self._config.get(CONF_CLIMATE_HUMIDITY))
        self.schedule_update_ha_state()

    @property
    def supported_features(self) -> int:
        """Flag supported features."""
        supports = 0
        if self.has_config(CONF_CLIMATE_TEMPERATURE):
            supports |= SUPPORT_TARGET_TEMPERATURE
        if self.has_config(CONF_CLIMATE_AUX_HEAT):
            supports |= SUPPORT_AUX_HEAT
        if self.has_config(CONF_CLIMATE_CURRENT_HUMIDITY) or self.has_config(CONF_CLIMATE_CURRENT_HUMIDITY):
            supports |= SUPPORT_TARGET_HUMIDITY
        return supports

    def status_updated(self):
        """Get state of Tuya CLIMATE."""
        state=HVAC_MODES[0]
        if self.has_config(CONF_CLIMATE_TEMPERATURE):
           if  self.dps_conf(CONF_CLIMATE_HEAT_COLD) and self.dps(self._dp_id):
               state=HVAC_MODES[2]
           elif  not self.dps_conf(CONF_CLIMATE_HEAT_COLD) and self.dps(self._dp_id):
               state=HVAC_MODES[1]
           else:
               state=HVAC_MODES[0]
           if self.dps_conf(CONF_CLIMATE_CURRENT)>70:
              self._current_temperature = self.dps_conf(CONF_CLIMATE_CURRENT)/10
           else:
              self._current_temperature = self.dps_conf(CONF_CLIMATE_CURRENT)
           self._hvac_mode = state
           self._target_temperature = self.dps_conf(CONF_CLIMATE_TEMPERATURE)
        if self.has_config(CONF_CLIMATE_TEMPERATURE_HIGH):
            self._max_temp = self.dps_conf(CONF_CLIMATE_TEMPERATURE_HIGH)
        if self.has_config(CONF_CLIMATE_TEMPERATURE_LOW):
            self._min_temp = self.dps_conf(CONF_CLIMATE_TEMPERATURE_LOW)
        if self.has_config(CONF_CLIMATE_AUX_HEAT):
            self._is_aux_heat = self.dps_conf(CONF_CLIMATE_AUX_HEAT)
        if self.has_config(CONF_CLIMATE_CURRENT_HUMIDITY) :
            self._current_humidity = self.dps_conf(CONF_CLIMATE_CURRENT_HUMIDITY)
        if self.has_config(CONF_CLIMATE_HUMIDITY) :
            self._target_humidity = self.dps_conf(CONF_CLIMATE_HUMIDITY)

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

on const.py file add

# climate
CONF_CLIMATE_HEAT_COLD = "heat_cold"
CONF_CLIMATE_CURRENT = "current_temperature"
CONF_CLIMATE_TEMPERATURE = "temperature_target"
CONF_CLIMATE_TEMPERATURE_LOW = "min_temp_id"
CONF_CLIMATE_TEMPERATURE_HIGH = "max_temp_id"
CONF_CLIMATE_AUX_HEAT = "auxillary_heat"
CONF_CLIMATE_HUMIDITY = "humidity_target"
CONF_CLIMATE_CURRENT_HUMIDITY = "current_humidity"

update to:

# Platforms in this list must support config flows
PLATFORMS = ["binary_sensor", "climate" , "cover", "fan", "light", "sensor", "switch" ]

on traduction files § data (2 area) info , please put the symbol , at the end on your last line exemple : previews "textbody": "Textbody" post "textbody": "Textbody" , and text below

                    "heat_cold": "Set Heat/Cold Modes",
                    "current_temperature":  "Temperature Sensor",                   
                    "temperature_target":  "Temperature Target",   
                    "min_temp_id":  "Minimum Temperature", 
                    "max_temp_id":  "Maximum Temperature",
                    "auxillary_heat": "Auxillary Heat",
                    "humidity_target": "Humidity Target", 
                    "current_humidity": "Humidity Sensor"

on pytuya.init.py line 502 , update text to

        self.dps_cache = {}
        ranges = [(2, 11), (11, 21), (21, 31), (31, 41), (100, 111)]

please advice heater config

Kh3nsu commented 2 years ago

Hey thank you very much for this :) Is it maybe possible to add on/off? I also found a Socket thermostat for my infrared heater: https://smile.amazon.de/-/en/gp/product/B09169PPD9/ref=ask_ql_qh_dp_hza Do you think this one is going to work?

caramot commented 2 years ago

hello wolfsWelpe please select at the first for parameter ID , the parameter ON/Off on your thermical module. and for you, it's not necessary to put value on parameter "set heat/cold modes" please inform me in case problem , i add comment on the code for the first ID

Kh3nsu commented 2 years ago

Okay thanks I will try as soon as I receive the thermostat :)

Locoblade commented 2 years ago

Thanks for this, someone might learn from my slight mistake copy/pasting in the code into the translation/traduction (French?) files entry where I didn't put a comma on the end of the existing values so when I copy/pasted in the new code it was missing a comma half way through.

Anyway having fixed that I've got it so that it now detects my air con unit but I'm struggling with the very last part setting up the entity by re-running the LocalTuya integration install. Is there a way to work out what attributes are associated with which HVAC setting? I've worked out the current temperature and I thought I'd found the target temperature but it's an order of magnitude out (180c rather than 18c). I have the same device installed via the Tuya cloud app and obviously have access to it via Tuya IOT so I was wondering if there was a way to work them all out from either of those sources?

caramot commented 2 years ago

hello, to manage the configuration of your device , the best way are to create a screen shoot of ID value , change parameter on your device , and compare delta . i don't have best way , sorry ps i add comment for problem of comma in the code , thank you

ruurdjan commented 2 years ago

I'm using an Eurom heater - mapping of fields to values is all over the place. One of the boolean switches seems to reset or crash the device... I can't seem to get this to work in any useable form. Some device details:

{
    "result": {
        "category": "qn",
        "functions": [
            {
                "code": "temp_set",
                "lang_config": {
                    "unit": "℃"
                },
                "name": "Set Temp",
                "type": "Integer",
                "values": "{"unit":"℃","min":0,"max":37,"scale":0,"step":1}"
            },
            {
                "code": "switch",
                "lang_config": {
                    "false": "OFF",
                    "true": "ON"
                },
                "name": "Power",
                "type": "Boolean",
                "values": "{}"
            }
        ],
        "status": [
            {
                "code": "temp_current",
                "lang_config": {
                    "unit": "℃"
                },
                "name": "Current Temp",
                "type": "Integer",
                "values": "{"unit":"℃","min":-9,"max":99,"scale":0,"step":1}"
            },
            {
                "code": "temp_set",
                "lang_config": {
                    "unit": "℃"
                },
                "name": "Set Temp",
                "type": "Integer",
                "values": "{"unit":"℃","min":0,"max":37,"scale":0,"step":1}"
            },
            {
                "code": "switch",
                "lang_config": {
                    "false": "OFF",
                    "true": "ON"
                },
                "name": "Power",
                "type": "Boolean",
                "values": "{}"
            }
        ]
    },
    "success": true,
    "t": 1641473662033
}

image

caramot commented 2 years ago

Hello, could you try , to remove /10 on this line self._current_temperature = self.dps_conf(CONF_CLIMATE_CURRENT)/10 i think to removed your problem

geroulas commented 2 years ago

Hello, help with Hysen HY08-1 Tuya Thermostat. I got most of DPs sorted out, but nothing works, when I set it up via Entity Configuration. Can I set this up using configuration.yaml? Create a climate entity and point to ids?

ID | REPRESENTS | VALUES -- | -- | -- ID1 | ON / OFF | True, Flase ID2 | TARGET TEMP (x10) | 210 ID3 | CURRENT TEMP (x10) | 185 ID4 | MODE | Manual, Program, Holiday ID6 | CHILD LOCK | True, Flase ID12 | ? | 0 ID101 | ? | True, Flase ID102 | HVAC CURRENT ACTION | True, Flase ID103 | ? | 0 ID104 | HOLIDAY MODE (DAY) | int 1 to 30 ID105 | HOLIDAY MODE (TEMP) | int 10 to 30 ID106 | ? | True, Flase ID107 | ? | True, Flase ID108 | ? | True, Flase ID109 | TEMP CALIBRATION (x10) | -20 ID110 | INT SENSOR DEADZONE (x10) | 5 ID111 | EXT SENSOR DEADZONE | 2 ID112 | HIGH TEMP PROTECTION | 45 ID113 | LOW TEMP PROTECTION | 6 ID114 | MAX TEMP LIMIT | 30 ID115 | MIN TEMP LIMIT | 10 ID116 | TEMPERATURE SENSOR | in, ext, all ID117 | DEVICE STATE ON POWER | Off, keep, On ID118 | PROGRAM TYPE | 2days, 1days, 0days
caramot commented 2 years ago

hello, i don't have simple solution for solve unit for target temp, x1 or x10 the official climate module have this option sorry