Skip to content

Commit

Permalink
Merge pull request #11 from EnzoD86/2024.8.2
Browse files Browse the repository at this point in the history
2024.8.2
  • Loading branch information
EnzoD86 authored Aug 14, 2024
2 parents c07420c + ad21c85 commit 02b5edc
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 333 deletions.
36 changes: 9 additions & 27 deletions custom_components/tuya_smart_ir_ac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@
import voluptuous as vol
import logging
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_registry import async_migrate_entries
from homeassistant.core import callback
from homeassistant.const import Platform
from .tuya_connector import TuyaOpenAPI
from .const import (
DOMAIN,
PLATFORMS,
CLIENT,
COORDINATOR,
CONF_ACCESS_ID,
CONF_ACCESS_SECRET,
CONF_TUYA_COUNTRY,
TUYA_API_CLIENT,
TUYA_ENDPOINTS
)

from .api import TuyaAPI
from .coordinator import TuyaCoordinator

_LOGGER = logging.getLogger(__package__)

Expand Down Expand Up @@ -48,32 +47,15 @@ async def async_setup(hass, config):
_LOGGER.error("Tuya Open API login error")
return False

api = TuyaAPI(hass, client)
coordinator = TuyaCoordinator(hass, api)

hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][TUYA_API_CLIENT] = client
hass.data[DOMAIN][CLIENT] = client
hass.data[DOMAIN][COORDINATOR] = coordinator
return True

async def async_setup_entry(hass, config_entry):
#TODO: remove in next release!
try:
_LOGGER.debug("Update unique_id")

infrared_id = config_entry.data.get("infrared_id")
climate_id = config_entry.data.get("climate_id")
new_unique_id = f"{infrared_id}_{climate_id}"

@callback
def update_unique_id(entity_entry):
"""Update unique ID of entity entry."""
return {
"new_unique_id": new_unique_id
}

await async_migrate_entries(hass, config_entry.entry_id, update_unique_id)
hass.config_entries.async_update_entry(config_entry)
except Exception as e:
pass
#################################

await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
config_entry.async_on_unload(config_entry.add_update_listener(async_update_entry))
return True
Expand Down
72 changes: 42 additions & 30 deletions custom_components/tuya_smart_ir_ac/api.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,73 @@
import logging
from .const import DOMAIN, TUYA_API_CLIENT


_LOGGER = logging.getLogger(__package__)


class TuyaAPI:
def __init__(self, hass, infrared_id, climate_id):
def __init__(self, hass, client):
self._hass = hass
self._client = hass.data.get(DOMAIN).get(TUYA_API_CLIENT)
self._infrared_id = infrared_id
self._climate_id = climate_id
self._client = client

async def async_fetch_status(self):
url = f"/v2.0/infrareds/{self._infrared_id}/remotes/{self._climate_id}/ac/status"
async def async_fetch_all_data(self, climate_ids):
try:
url = f"/v1.0/cloud/rc/infrared/ac/status/batch?device_ids={",".join(climate_ids)}"
_LOGGER.debug(f"API fetch_all_data url: {url}")
result = await self._hass.async_add_executor_job(self._client.get, url)
_LOGGER.debug(f"Climate {self._climate_id} fetch status response: {str(result)}")
_LOGGER.debug(f"API fetch_all_data response: {str(result)}")
if result.get("success"):
return result.get("result")
raise Exception(TuyaError("", result).to_dict())
raise Exception(TuyaDetails(url, "", result).to_dict())
except Exception as e:
_LOGGER.error(f"Error fetching status for climate {self._climate_id}: {e}")
return None
_LOGGER.error(f"Error fetching all data for climates {str(climate_ids)}: {e}")
raise Exception(e)

async def async_fetch_data(self, infrared_id, climate_id):
try:
url = f"/v2.0/infrareds/{infrared_id}/remotes/{climate_id}/ac/status"
_LOGGER.debug(f"API fetch_data url: {url}")
result = await self._hass.async_add_executor_job(self._client.get, url)
_LOGGER.debug(f"API fetch_data response: {str(result)}")
if result.get("success"):
return result.get("result")
raise Exception(TuyaDetails(url, "", result).to_dict())
except Exception as e:
_LOGGER.error(f"Error fetching data for climate {climate_id}: {e}")
raise Exception(e)

async def async_send_command(self, code, value):
url = f"/v2.0/infrareds/{self._infrared_id}/air-conditioners/{self._climate_id}/command"
command = { "code": code, "value": value }
async def async_send_command(self, infrared_id, climate_id, code, value):
try:
_LOGGER.debug(f"Climate {self._climate_id} send command request: {str(command)}")
url = f"/v2.0/infrareds/{infrared_id}/air-conditioners/{climate_id}/command"
_LOGGER.debug(f"API send_command url: {url}")
command = { "code": code, "value": value }
_LOGGER.debug(f"API send_command request: {command}")
result = await self._hass.async_add_executor_job(self._client.post, url, command)
_LOGGER.debug(f"Climate {self._climate_id} send command response: {str(result)}")
_LOGGER.debug(f"API send_command response: {str(result)}")
if not result.get("success"):
raise Exception(TuyaError(command, result).to_dict())
raise Exception(TuyaDetails(url, command, result).to_dict())
except Exception as e:
_LOGGER.error(f"Error sending command to climate {self._climate_id}: {e}")
_LOGGER.error(f"Error sending command to climate {climate_id}: {e}")
raise Exception(e)

async def async_send_multiple_command(self, power, mode, temp, wind):
url = f"/v2.0/infrareds/{self._infrared_id}/air-conditioners/{self._climate_id}/scenes/command"
command = { "power": power, "mode": mode, "temp": temp, "wind": wind }
async def async_send_multiple_command(self, infrared_id, climate_id, power, mode, temp, wind):
try:
_LOGGER.debug(f"Climate {self._climate_id} send multiple command request: {str(command)}")
url = f"/v2.0/infrareds/{infrared_id}/air-conditioners/{climate_id}/scenes/command"
_LOGGER.debug(f"API send_multiple_command url: {url}")
command = { "power": power, "mode": mode, "temp": temp, "wind": wind }
_LOGGER.debug(f"API send_multiple_command request: {command}")
result = await self._hass.async_add_executor_job(self._client.post, url, command)
_LOGGER.debug(f"Climate {self._climate_id} send multiple command response: {str(result)}")
_LOGGER.debug(f"API send_multiple_command response: {str(result)}")
if not result.get("success"):
raise Exception(TuyaError(command, result).to_dict())
return result
raise Exception(TuyaDetails(url, command, result).to_dict())
except Exception as e:
_LOGGER.error(f"Error sending multiple command to climate {self._climate_id}: {e}")
_LOGGER.error(f"Error sending multiple command to climate {climate_id}: {e}")
raise Exception(e)


class TuyaError(object):
def __init__(self, request, response):
class TuyaDetails(object):
def __init__(self, url, request, response):
self.url = url
self.request = request
self.response = response

def to_dict(self):
return vars(self)
return vars(self)
83 changes: 47 additions & 36 deletions custom_components/tuya_smart_ir_ac/climate.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import voluptuous as vol
import logging
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.core import callback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
FAN_AUTO,
Expand All @@ -15,10 +13,12 @@
UnitOfTemperature,
STATE_UNKNOWN,
STATE_UNAVAILABLE,
CONF_NAME,
CONF_UNIQUE_ID
CONF_NAME
)
from .const import (
DOMAIN,
MANUFACTURER,
COORDINATOR,
CONF_INFRARED_ID,
CONF_CLIMATE_ID,
CONF_TEMPERATURE_SENSOR,
Expand All @@ -32,18 +32,17 @@
DEFAULT_HVAC_MODES,
DEFAULT_FAN_MODES
)
from .service import TuyaService


_LOGGER = logging.getLogger(__package__)


async def async_setup_entry(hass, config_entry, async_add_entities):
async_add_entities([TuyaClimate(hass, config_entry.data)])
coordinator = hass.data.get(DOMAIN).get(COORDINATOR)
async_add_entities([TuyaClimate(hass, config_entry.data, coordinator)])


class TuyaClimate(ClimateEntity, RestoreEntity):
def __init__(self, hass, config):
class TuyaClimate(ClimateEntity, CoordinatorEntity):
def __init__(self, hass, config, coordinator):
self._infrared_id = config.get(CONF_INFRARED_ID)
self._climate_id = config.get(CONF_CLIMATE_ID)
self._name = config.get(CONF_NAME)
Expand All @@ -54,12 +53,12 @@ def __init__(self, hass, config):
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._service = TuyaService(hass, self._infrared_id, self._climate_id)

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

self._hvac_mode = HVACMode.OFF
self._fan_mode = FAN_AUTO
self._target_temperature = 0
self._fan_mode = FAN_AUTO

@property
def name(self):
Expand All @@ -69,6 +68,19 @@ def name(self):
def unique_id(self):
return f"{self._infrared_id}_{self._climate_id}"

@property
def device_info(self):
return {
"name": self._name,
"identifiers": {(DOMAIN, self._climate_id)},
"via_device": (DOMAIN, self._infrared_id),
"manufacturer": MANUFACTURER
}

@property
def available(self):
return self.coordinator.is_available(self._climate_id)

@property
def temperature_unit(self):
return UnitOfTemperature.CELSIUS
Expand All @@ -85,7 +97,7 @@ def min_temp(self):
def max_temp(self):
return self._max_temp

@property
@property
def target_temperature_step(self):
return self._temp_step

Expand Down Expand Up @@ -119,38 +131,37 @@ def fan_mode(self):
def fan_modes(self):
return self._fan_modes

async def async_added_to_hass(self):
last_state = await self.async_get_last_state()
if last_state:
self._hvac_mode = last_state.state
self._fan_mode = last_state.attributes.get("fan_mode")
self._target_temperature = last_state.attributes.get("temperature")

async def async_update(self):
status = await self._service.async_fetch_status()
if (status and
(self._hvac_mode != status.hvac_mode
or self._fan_mode != status.fan_mode
or self._target_temperature != status.temperature)):
self._hvac_mode = status.hvac_mode
self._fan_mode = status.fan_mode
self._target_temperature = status.temperature
self.async_write_ha_state()
@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()

async def async_turn_on(self):
_LOGGER.info(f"{self.entity_id} turn on")
await self._service.async_turn_on()
await self.coordinator.async_turn_on(self._infrared_id, self._climate_id)
self._handle_coordinator_update()

async def async_turn_off(self):
_LOGGER.info(f"{self.entity_id} turn off")
await self.coordinator.async_turn_off(self._infrared_id, self._climate_id)
self._handle_coordinator_update()

async def async_set_temperature(self, **kwargs):
temperature = kwargs.get("temperature")
if temperature is not None:
_LOGGER.info(f"{self.entity_id} setting temperature to {temperature}")
await self._service.async_set_temperature(temperature)
await self.coordinator.async_set_temperature(self._infrared_id, self._climate_id, temperature)
self._handle_coordinator_update()

async def async_set_fan_mode(self, fan_mode):
_LOGGER.info(f"{self.entity_id} setting fan mode to {fan_mode}")
await self._service.async_set_fan_mode(fan_mode)
await self.coordinator.async_set_fan_mode(self._infrared_id, self._climate_id, fan_mode)
self._handle_coordinator_update()

async def async_set_hvac_mode(self, hvac_mode):
_LOGGER.info(f"{self.entity_id} setting hvac mode to {hvac_mode}")
await self._service.async_set_hvac_mode(hvac_mode, self._target_temperature, FAN_AUTO)
await self.coordinator.async_set_hvac_mode(self._infrared_id, self._climate_id, hvac_mode, self._target_temperature, FAN_AUTO)
self._handle_coordinator_update()
Loading

0 comments on commit 02b5edc

Please sign in to comment.