Skip to content

Commit

Permalink
Merge pull request #18 from EnzoD86/2024.8.4
Browse files Browse the repository at this point in the history
2024.8.4
  • Loading branch information
EnzoD86 authored Aug 30, 2024
2 parents 3c04904 + 2c44ac1 commit 5a83f44
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 73 deletions.
86 changes: 56 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,70 @@ This is a custom integration to control IR-based air conditioners from Tuya via

> **This repository was cloned from [DavidIlie's project](https://github.com/DavidIlie/tuya-smart-ir-ac) because it had not been updated for some time.**
## Installation using HACS
We recommend installing via Home Assistant Community Store (HACS) to always receive the latest integration updates.
Add this [repository](https://github.com/EnzoD86/tuya-smart-ir-ac) to your custom repositories or click the button below (requires My Homeassistant setup).

[![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=EnzoD86&repository=tuya-smart-ir-ac&category=integration)

## Retrieve correct information from Tuya IoT website
Before you start setting up the integration, you will need some information that can be retrieved from the Tuya IoT platform.
The steps to retrieve the information you need are the following:
- connect to https://platform.tuya.com ;
- from the left menu select Cloud -> Development;
- create a project or select the existing one (making sure it is connected to SmartLife);
- from the Overview tab retrieve the Access ID, Access Secret and Data Center data of the project (which will be used for the installation of the platform);
- from the Service API tab, verify that the following APIs are active (verify by clicking on View details, the API status is In Service for each of them):
- IoT Core;
- IR Control Hub Open Service.
- from the Devices tab you will see all your devices connected to SmartLife and you will have to search for the following devices:
- the HUB IR device (called InfraredID by the integration);
- the air conditioner connected to the HUB (called ClimateID by the integration).

The last point must be repeated for each air conditioner that must be configured.

## Platform configuration
Add the following sections in your configuration.yaml and restart HA (Tuya Access ID, Tuya Access Secret can be found on the [Tuya IoT Website](https://platform.tuya.com/)):

Add the following sections in your configuration.yaml and restart HA (Tuya Access ID, Tuya Access Secret can be found on the Tuya IoT Website):

| Name | Type | Description | Required |
| -------------------- | -------- | -------------------------------- | -------- |
| access_id | `string` | Tuya access ID. | Yes |
| access_secret | `string` | Tuya access secret. | Yes |
| country | `string` | Tuya country API: EU, US, IN, CN | Yes |


### Country/Data center API

| ID | Data center |
| ----- | ----------- |
| EU | Europe |
| US | America |
| IN | India |
| CN | China |

| Name | Type | Description | Required |
| -------------------- | -------- | ------------------------------------------------------------------- | -------- |
| access_id | `string` | Tuya access ID. | Yes |
| access_secret | `string` | Tuya access secret. | Yes |
| country | `string` | Tuya country API: EU (Europe), US (America), IN (India), CN (China) | Yes |

### Example

```yaml
tuya_smart_ir_ac:
access_id: "tuya_access_id_example"
access_secret: "tuya_access_secret_example"
country: "EU"
```
Then you can add the "Tuya Smart IR Air Conditioners" integration from the web interface to configure your air conditioners.
You need to retrieve your Climate ID (Device ID of your air conditioning) and Infrared ID (Device ID of your IR HUB) on the Tuya IoT website.
## Integration configuration
After the platform has been configured, you can add air conditioners using the Integrations configuration UI.
Go to Settings / Devices & Services and press the Add Integration button, or click the shortcut button below (requires My Homeassistant configured).
[![Add Integration to your Home Assistant
instance.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=tuya_smart_ir_ac)
The interface will show a form to fill in with the following information:
### Steps to retrieve the information needed for correct configuration
- connect to https://platform.tuya.com/
- from the left menu select Cloud -> Development
- open your cloud project (which you will have previously created and connected to the SmartLife app) by clicking on the "Open Project" button
- in the "Overview" tab you have the "Authorization Key" section where you will find the Access ID and the Access Secret that you must enter in the configuration.yaml
- select the "Devices" tab and you will see the list of all your devices connected to SmartLife
- search in the list of your devices the ID of the infrared hub and the ID of the air conditioner (which you will have previously created on SmartLife)
- in the "Service API" tab make sure that the "IR Control Hub Open Service" API is active
| Name | Description | Required |
| ------------------------------------- | -------------------------------------------------------------------------------------------------- | -------- |
| Infrared ID | IR HUB Device ID (retrieved from Tuya Platform). | Yes |
| Climate ID | Air Conditioner Device ID (retrieved from Tuya Platform). | Yes |
| Air conditioner name | Name of the device that will be displayed in Home Assistant. | Yes |
| Temperature sensor | Name of the temperature sensor to pair with the device. | No |
| Humidity sensor | Name of the humidity sensor to pair with the device. | No |
| Minimum temperature | Minimum set point availabl supported by the device. | No |
| Maximum temperature | Maximum set point available supported by the device. | No |
| Step temperature | Step size for temperature set point supported by the device. | No |
| HVAC modes supported | HVAC modes supported by the device. | No |
| FAN modes supported | FAN modes supported by the device. | No |
| *Set minimum temperature in dry mode* | *Set temperature to 16° when DRY MODE is selected (fixes compatibility issues with some devices).* | No |
| *Set minimum fan mode in dry mode* | *Set fan speed to LOW when DRY MODE is selected (fixes compatibility issues with some devices).* | No |
> ***The last two options should only be used when you are having problems with DRY mode; if you are unable to change modes, try enabling one or both flags.***
# Debug
It is possible to activate debug mode by adding the following lines in your configuration.yaml file:
Expand All @@ -66,5 +88,9 @@ Home Assistant needs to be restarted after this change.


## Contributions are welcome!
If you have ideas to contribute to the project, open a pull request and we will evaluate together how to implement the improvement. Thanks!

## Support me
I dedicate my free time to the development and support for this integration, if you appreciate my work and want to support me, you can buy me a coffee. Thanks!

If this repository can help anyone, any contribution is welcome.
<a href="https://www.buymeacoffee.com/enzod86" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
41 changes: 31 additions & 10 deletions custom_components/tuya_smart_ir_ac/climate.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import logging
from homeassistant.core import callback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
FAN_AUTO,
FAN_LOW,
HVACMode,
ClimateEntityFeature,
DEFAULT_MIN_TEMP,
DEFAULT_MAX_TEMP
ClimateEntityFeature
)
from homeassistant.const import (
UnitOfTemperature,
Expand All @@ -28,9 +28,15 @@
CONF_TEMP_STEP,
CONF_HVAC_MODES,
CONF_FAN_MODES,
CONF_DRY_MIN_TEMP,
CONF_DRY_MIN_FAN,
DEFAULT_MIN_TEMP,
DEFAULT_MAX_TEMP,
DEFAULT_PRECISION,
DEFAULT_HVAC_MODES,
DEFAULT_FAN_MODES
DEFAULT_FAN_MODES,
DEFAULT_DRY_MIN_TEMP,
DEFAULT_DRY_MIN_FAN
)

_LOGGER = logging.getLogger(__package__)
Expand All @@ -41,7 +47,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
async_add_entities([TuyaClimate(hass, config_entry.data, coordinator)])


class TuyaClimate(ClimateEntity, CoordinatorEntity):
class TuyaClimate(ClimateEntity, RestoreEntity, CoordinatorEntity):
def __init__(self, hass, config, coordinator):
self._infrared_id = config.get(CONF_INFRARED_ID)
self._climate_id = config.get(CONF_CLIMATE_ID)
Expand All @@ -53,6 +59,8 @@ def __init__(self, hass, config, coordinator):
self._temp_step = config.get(CONF_TEMP_STEP, DEFAULT_PRECISION)
self._hvac_modes = config.get(CONF_HVAC_MODES, DEFAULT_HVAC_MODES)
self._fan_modes = config.get(CONF_FAN_MODES, DEFAULT_FAN_MODES)
self._dry_min_temp = config.get(CONF_DRY_MIN_TEMP, DEFAULT_DRY_MIN_TEMP)
self._dry_min_fan = config.get(CONF_DRY_MIN_FAN, DEFAULT_DRY_MIN_FAN)

super().__init__(coordinator, context=self._climate_id)

Expand Down Expand Up @@ -131,13 +139,23 @@ def fan_mode(self):
def fan_modes(self):
return self._fan_modes

async def async_added_to_hass(self):
await super().async_added_to_hass()

last_state = await self.async_get_last_state()
if last_state and last_state.state not in [STATE_UNKNOWN, STATE_UNAVAILABLE]:
self._hvac_mode = last_state.state
self._target_temperature = last_state.attributes.get("temperature")
self._fan_mode = last_state.attributes.get("fan_mode")

@callback
def _handle_coordinator_update(self):
data = self.coordinator.data.get(self._climate_id)
self._hvac_mode = data.hvac_mode if data.power else HVACMode.OFF
self._target_temperature = data.temperature
self._fan_mode = data.fan_mode
self.async_write_ha_state()
if data:
self._hvac_mode = data.hvac_mode if data.power else HVACMode.OFF
self._target_temperature = data.temperature
self._fan_mode = data.fan_mode
self.async_write_ha_state()

async def async_turn_on(self):
_LOGGER.info(f"{self.entity_id} turn on")
Expand All @@ -163,5 +181,8 @@ async def async_set_fan_mode(self, fan_mode):

async def async_set_hvac_mode(self, hvac_mode):
_LOGGER.info(f"{self.entity_id} setting hvac mode to {hvac_mode}")
await self.coordinator.async_set_hvac_mode(self._infrared_id, self._climate_id, hvac_mode, self._target_temperature, FAN_AUTO)
temperature = (DEFAULT_MIN_TEMP if hvac_mode is HVACMode.DRY and self._dry_min_temp
else (self._min_temp if self._target_temperature < self._min_temp else self._target_temperature))
fan_mode = FAN_LOW if hvac_mode is HVACMode.DRY and self._dry_min_fan else FAN_AUTO
await self.coordinator.async_set_hvac_mode(self._infrared_id, self._climate_id, hvac_mode, temperature, fan_mode)
self._handle_coordinator_update()
77 changes: 53 additions & 24 deletions custom_components/tuya_smart_ir_ac/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
CONF_NAME,
Platform
)
from homeassistant.components.climate.const import (
DEFAULT_MIN_TEMP,
DEFAULT_MAX_TEMP
)
from .const import (
DOMAIN,
CLIENT,
Expand All @@ -23,9 +19,15 @@
CONF_TEMP_STEP,
CONF_HVAC_MODES,
CONF_FAN_MODES,
CONF_DRY_MIN_TEMP,
CONF_DRY_MIN_FAN,
DEFAULT_MIN_TEMP,
DEFAULT_MAX_TEMP,
DEFAULT_PRECISION,
DEFAULT_HVAC_MODES,
DEFAULT_FAN_MODES
DEFAULT_FAN_MODES,
DEFAULT_DRY_MIN_TEMP,
DEFAULT_DRY_MIN_FAN
)
from .api import TuyaAPI

Expand All @@ -49,8 +51,8 @@ async def async_step_user(self, user_input):

if await TuyaAPI(self.hass, client).async_fetch_data(infrared_id, climate_id):
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
else:
errors["base"] = "connection"

errors["base"] = "connection"

data = {}
data.update(required_data())
Expand Down Expand Up @@ -94,11 +96,13 @@ def optional_data(config=None):
if config is None:
temperature_sensor = vol.Optional(CONF_TEMPERATURE_SENSOR)
humidity_sensor = vol.Optional(CONF_HUMIDITY_SENSOR)
default_temp_min = DEFAULT_MIN_TEMP
default_temp_max = DEFAULT_MAX_TEMP
default_temp_step = DEFAULT_PRECISION
default_hvac_modes = DEFAULT_HVAC_MODES
default_fan_modes = DEFAULT_FAN_MODES
temp_min = vol.Optional(CONF_TEMP_MIN, default=DEFAULT_MIN_TEMP)
temp_max = vol.Optional(CONF_TEMP_MAX, default=DEFAULT_MAX_TEMP)
temp_step = vol.Optional(CONF_TEMP_STEP, default=DEFAULT_PRECISION)
hvac_modes = vol.Optional(CONF_HVAC_MODES, default=DEFAULT_HVAC_MODES)
fan_modes = vol.Optional(CONF_FAN_MODES, default=DEFAULT_FAN_MODES)
dry_min_temp = vol.Optional(CONF_DRY_MIN_TEMP, default=DEFAULT_DRY_MIN_TEMP)
dry_min_fan = vol.Optional(CONF_DRY_MIN_FAN, default=DEFAULT_DRY_MIN_FAN)
else:
if config.get(CONF_TEMPERATURE_SENSOR, None) is None:
temperature_sensor = vol.Optional(CONF_TEMPERATURE_SENSOR)
Expand All @@ -109,12 +113,14 @@ def optional_data(config=None):
humidity_sensor = vol.Optional(CONF_HUMIDITY_SENSOR)
else:
humidity_sensor = vol.Optional(CONF_HUMIDITY_SENSOR, default=config.get(CONF_HUMIDITY_SENSOR))

default_temp_min = config.get(CONF_TEMP_MIN, DEFAULT_MIN_TEMP)
default_temp_max = config.get(CONF_TEMP_MAX, DEFAULT_MAX_TEMP)
default_temp_step = config.get(CONF_TEMP_STEP, DEFAULT_PRECISION)
default_hvac_modes = config.get(CONF_HVAC_MODES, DEFAULT_HVAC_MODES)
default_fan_modes = config.get(CONF_FAN_MODES, DEFAULT_FAN_MODES)

temp_min = vol.Optional(CONF_TEMP_MIN, default=config.get(CONF_TEMP_MIN, DEFAULT_MIN_TEMP))
temp_max = vol.Optional(CONF_TEMP_MAX, default=config.get(CONF_TEMP_MAX, DEFAULT_MAX_TEMP))
temp_step = vol.Optional(CONF_TEMP_STEP, default=config.get(CONF_TEMP_STEP, DEFAULT_PRECISION))
hvac_modes = vol.Optional(CONF_HVAC_MODES, default=config.get(CONF_HVAC_MODES, DEFAULT_HVAC_MODES))
fan_modes = vol.Optional(CONF_FAN_MODES, default=config.get(CONF_FAN_MODES, DEFAULT_FAN_MODES))
dry_min_temp = vol.Optional(CONF_DRY_MIN_TEMP, default=config.get(CONF_DRY_MIN_TEMP, DEFAULT_DRY_MIN_TEMP))
dry_min_fan = vol.Optional(CONF_DRY_MIN_FAN, default=config.get(CONF_DRY_MIN_FAN, DEFAULT_DRY_MIN_FAN))

return {
temperature_sensor: selector.EntitySelector(
Expand All @@ -131,21 +137,44 @@ def optional_data(config=None):
multiple=False
)
),
vol.Optional(CONF_TEMP_MIN, default=default_temp_min): vol.Coerce(float),
vol.Optional(CONF_TEMP_MAX, default=default_temp_max): vol.Coerce(float),
vol.Optional(CONF_TEMP_STEP, default=default_temp_step): vol.Coerce(float),
vol.Optional(CONF_HVAC_MODES, default=default_hvac_modes): selector.SelectSelector(
temp_min: selector.NumberSelector(
selector.NumberSelectorConfig(
min=DEFAULT_MIN_TEMP,
max=DEFAULT_MAX_TEMP,
step=1,
mode=selector.NumberSelectorMode.BOX
)
),
temp_max: selector.NumberSelector(
selector.NumberSelectorConfig(
min=DEFAULT_MIN_TEMP,
max=DEFAULT_MAX_TEMP,
step=1,
mode=selector.NumberSelectorMode.BOX
)
),
temp_step: selector.NumberSelector(
selector.NumberSelectorConfig(
min=0.1,
max=1,
step=0.1,
mode=selector.NumberSelectorMode.BOX
)
),
hvac_modes: selector.SelectSelector(
selector.SelectSelectorConfig(
options=DEFAULT_HVAC_MODES,
multiple=True,
mode=selector.SelectSelectorMode.DROPDOWN
)
),
vol.Optional(CONF_FAN_MODES, default=default_fan_modes): selector.SelectSelector(
fan_modes: selector.SelectSelector(
selector.SelectSelectorConfig(
options=DEFAULT_FAN_MODES,
multiple=True,
mode=selector.SelectSelectorMode.DROPDOWN
)
)
),
dry_min_temp: selector.BooleanSelector(),
dry_min_fan: selector.BooleanSelector()
}
8 changes: 7 additions & 1 deletion custom_components/tuya_smart_ir_ac/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
COORDINATOR = "COORDINATOR"
FIRST_UPDATE = 5
UPDATE_INTERVAL = 60
UPDATE_TIMEOUT = 10
UPDATE_TIMEOUT = 15
PLATFORMS = [Platform.CLIMATE]

CONF_ACCESS_ID = "access_id"
Expand All @@ -29,8 +29,14 @@
CONF_TEMP_STEP = "temp_step"
CONF_HVAC_MODES = "hvac_modes"
CONF_FAN_MODES = "fan_modes"
CONF_DRY_MIN_TEMP = "dry_min_temp"
CONF_DRY_MIN_FAN = "dry_min_fan"

DEFAULT_MIN_TEMP = 16
DEFAULT_MAX_TEMP = 30
DEFAULT_PRECISION = 1.0
DEFAULT_DRY_MIN_TEMP = False
DEFAULT_DRY_MIN_FAN = False

DEFAULT_HVAC_MODES = [
HVACMode.COOL,
Expand Down
2 changes: 1 addition & 1 deletion custom_components/tuya_smart_ir_ac/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self, hass, api):
self._first_update = True

def is_available(self, climate_id):
return self.last_update_success and self.data and self.data.get(climate_id, None) is not None
return self.data and self.data.get(climate_id, None) is not None

async def async_fetch_data(self, climate_ids):
all_data = await self._api.async_fetch_all_data(climate_ids)
Expand Down
4 changes: 2 additions & 2 deletions custom_components/tuya_smart_ir_ac/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/EnzoD86/tuya-smart-ir-ac/issues",
"requirements": ["tuya-connector-python==0.1.2"],
"version": "2024.8.3"
}
"version": "2024.8.4"
}
8 changes: 6 additions & 2 deletions custom_components/tuya_smart_ir_ac/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"max_temp": "Maximum temperature",
"temp_step": "Step temperature",
"hvac_modes": "HVAC modes supported",
"fan_modes": "FAN modes supported"
"fan_modes": "FAN modes supported",
"dry_min_temp": "Set minimum temperature in dry mode",
"dry_min_fan": "Set minimum fan mode in dry mode"
},
"description": "Configuration",
"title": "Tuya Smart IR Air Conditioners"
Expand All @@ -35,7 +37,9 @@
"max_temp": "Maximum temperature",
"temp_step": "Step temperature",
"hvac_modes": "HVAC modes supported",
"fan_modes": "FAN modes supported"
"fan_modes": "FAN modes supported",
"dry_min_temp": "Set minimum temperature in dry mode",
"dry_min_fan": "Set minimum fan speed in dry mode"
},
"description": "Configurazione",
"title": "Tuya Smart IR Air Conditioners"
Expand Down
Loading

0 comments on commit 5a83f44

Please sign in to comment.