Skip to content
Open
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -161,5 +161,5 @@ cython_debug/

*~
./

test.py
.DS_Store
28 changes: 25 additions & 3 deletions custom_components/nilan_cts600/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from .const import PLATFORMS
from .const import PLATFORMS, DATA_KEY, CONNECTION_TYPE_TCP


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the integration from a (UI) config entry."""
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):

async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return True
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

if unload_ok and DATA_KEY in hass.data:
# Determine the connection key
config = entry.data
connection_type = config.get("connection_type", CONNECTION_TYPE_TCP)

if connection_type == CONNECTION_TYPE_TCP:
host = config.get("host")
tcp_port = config.get("tcp_port", 502)
connection_key = f"tcp://{host}:{tcp_port}"
else:
connection_key = config.get("port")

# Remove the coordinator from hass.data and disconnect
if connection_key in hass.data[DATA_KEY]:
coordinator = hass.data[DATA_KEY].pop(connection_key)
if hasattr(coordinator, 'cts600') and coordinator.cts600:
coordinator.cts600.disconnect()

return unload_ok
64 changes: 38 additions & 26 deletions custom_components/nilan_cts600/button.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,63 @@
import logging

from homeassistant.core import HomeAssistant, callback
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.components.button import ButtonEntity, ButtonEntityDescription

from .coordinator import getCoordinator
from .nilan_cts600 import Key

_LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None:
""" foo """
_LOGGER.debug ("%s setup_entry: %s", __name__, entry.data)
await async_setup_platform (hass, entry.data, async_add_entities)

async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""foo"""
_LOGGER.debug("%s setup_entry: %s", __name__, entry.data)
await async_setup_platform(hass, entry.data, async_add_entities, entry_id=entry.entry_id)


async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
entry_id: str | None = None,
) -> None:
"""Set up the platform."""
coordinator = await getCoordinator (hass, config)
async_add_entities([CTS600Button (coordinator, key) for key in [Key.UP, Key.DOWN, Key.ENTER, Key.ESC, Key.ON, Key.OFF]])
coordinator = await getCoordinator(hass, config)
async_add_entities(
[
CTS600Button(coordinator, key, entry_id)
for key in [Key.UP, Key.DOWN, Key.ENTER, Key.ESC, Key.ON, Key.OFF]
]
)


class CTS600Button(CoordinatorEntity, ButtonEntity):
def __init__(self, coordinator, key) -> None:
"""Button entity for CTS600."""

_attr_has_entity_name = True

def __init__(self, coordinator, key, entry_id: str | None = None) -> None:
super().__init__(coordinator)
self.var_name = key.name.lower()
self._name = coordinator.name + " " + self.var_name
self._attr_device_info = coordinator.device_info
self.entity_description = ButtonEntityDescription(key=self.var_name, device_class=None)
self._attr_unique_id = f"serial-{self.coordinator.cts600.port}-{self.var_name}"
self.entity_description = ButtonEntityDescription(
key=self.var_name,
name=self.var_name.replace("_", " ").title(),
device_class=None,
)
# Use entry_id for stable unique_id
self._attr_unique_id = f"{entry_id}-{self.var_name}" if entry_id else f"{coordinator.name}-{self.var_name}"
self.key = key

@property
def name (self):
"""Return the name of the climate device."""
return self._name

async def async_press (self) -> None:
await self.coordinator.key (self.key)
async def async_press(self) -> None:
await self.coordinator.key(self.key)
self.coordinator.register_manual_activity()
self.coordinator.cts600.updateDisplay()
self.coordinator.async_set_updated_data(self.coordinator.data)


154 changes: 76 additions & 78 deletions custom_components/nilan_cts600/climate.py
Original file line number Diff line number Diff line change
@@ -1,78 +1,82 @@
import logging

from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import UnitOfTemperature

from homeassistant.components.climate import ClimateEntity, ClimateEntityDescription
from homeassistant.util.unit_conversion import TemperatureConverter
from homeassistant.components.climate.const import (
HVACMode,
ClimateEntityFeature,
HVACAction,
ClimateEntityFeature
HVACMode,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util.unit_conversion import TemperatureConverter

from .coordinator import getCoordinator

_LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None:
await async_setup_platform (hass, entry.data, async_add_entities)


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
await async_setup_platform(hass, entry.data, async_add_entities, entry_id=entry.entry_id)


async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
entry_id: str | None = None,
) -> None:
"""Set up the platform."""
coordinator = await getCoordinator (hass, config)
device = CTS600Climate (hass, coordinator)
coordinator = await getCoordinator(hass, config)
device = CTS600Climate(hass, coordinator, entry_id)
async_add_entities([device], update_before_add=True)

class CTS600Climate (CoordinatorEntity, ClimateEntity):
""" Provide the HA Climate interface. """

class CTS600Climate(CoordinatorEntity, ClimateEntity):
"""Provide the HA Climate interface."""

_mode_map = {
# Map CTS600 display text to HVACMode.
'HEAT': HVACMode.HEAT,
'COOL': HVACMode.COOL,
'AUTO': HVACMode.HEAT_COOL,
'OFF': HVACMode.OFF,
"HEAT": HVACMode.HEAT,
"COOL": HVACMode.COOL,
"AUTO": HVACMode.HEAT_COOL,
"OFF": HVACMode.OFF,
}
_mode_imap = {v:k for k,v in _mode_map.items()}
_mode_imap = {v: k for k, v in _mode_map.items()}
_action_map = {
# Map CTS600 display text to HVACAction.
'HEATING': HVACAction.HEATING,
'COOLING': HVACAction.COOLING,
'OFF': HVACAction.OFF,
"HEATING": HVACAction.HEATING,
"COOLING": HVACAction.COOLING,
"OFF": HVACAction.OFF,
}

def __init__ (self, hass, coordinator):
_attr_has_entity_name = True
_attr_name = "Climate"

def __init__(self, hass, coordinator, entry_id: str | None = None):
super().__init__(coordinator)

self.cts600 = coordinator.cts600
self._name = coordinator.name + " Climate Control"
self._attr_unique_id = f"serial-{self.cts600.port}-climate"
# Use entry_id for stable unique_id
self._attr_unique_id = f"{entry_id}-climate" if entry_id else f"{coordinator.name}-climate"

self._state = None
self._last_on_operation = None
self._fan_mode = None
self._air_condition_model = None

self._attr_device_info = coordinator.device_info
self.entity_description = ClimateEntityDescription(
key='nilan_cts600',
icon='mdi:hvac'
key="nilan_cts600", icon="mdi:hvac"
)
self._enable_turn_on_off_backwards_compatibility = False
_LOGGER.debug ('nilan INIT %s -> %s', self._name, self.entity_id)

@property
def name (self):
"""Return the name of the climate device."""
return self._name
_LOGGER.debug("nilan INIT %s -> %s", self._attr_name, self.entity_id)

@property
def min_temp(self):
Expand Down Expand Up @@ -100,11 +104,13 @@ def temperature_unit(self):
@property
def supported_features(self):
"""Return the set of supported features."""
return ClimateEntityFeature.TARGET_TEMPERATURE \
| ClimateEntityFeature.FAN_MODE \
| ClimateEntityFeature.TURN_ON \
return (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_ON
| ClimateEntityFeature.TURN_OFF

)

@property
def hvac_modes(self):
"""Return the list of available hvac modes."""
Expand All @@ -113,80 +119,72 @@ def hvac_modes(self):
@property
def hvac_mode(self):
"""Return hvac mode ie. heat, cool, fan only."""
cts600mode = self.cts600.data.get ('mode')
cts600mode = self.cts600.data.get("mode")
mode = self._mode_map.get(cts600mode, None) if cts600mode else None
_LOGGER.debug ('hvac mode %s -> %s', cts600mode, mode)
_LOGGER.debug("hvac mode %s -> %s", cts600mode, mode)
return mode

@property
def hvac_action(self):
"""Return hvac action ie. heat, cool, off."""
led = self.cts600.led()
if led == 'on':
cts600action = self.cts600.data.get ('status')
action = self._action_map.get(cts600action, None) if cts600action else None
return action
elif led == 'off':
return HVACAction.IDLE
else:
return None
cts600action = self.cts600.data.get("status")
return self._action_map.get(cts600action, None) if cts600action else None

@property
def fan_modes (self):
def fan_modes(self):
"""Return the list of available fan modes."""
return ['1', '2', '3', '4']
return ["1", "2", "3", "4"]

@property
def fan_mode (self):
def fan_mode(self):
"""Return the current fan speed."""
flow = self.cts600.data.get ('flow', None)
return str (flow) if flow else None
flow = self.cts600.data.get("flow", None)
return str(flow) if flow else None

@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self.cts600.data.get('thermostat', None)
return self.cts600.data.get("thermostat", None)

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

async def async_set_temperature (self, temperature=None, **kwargs):
async def async_set_temperature(self, temperature=None, **kwargs):
"""Set target temperature."""
_LOGGER.debug ('set fan_temperature %s', temperature)
await self.coordinator.setThermostat (int(temperature))
async def async_set_fan_mode (self, fan_mode):
_LOGGER.debug("set fan_temperature %s", temperature)
await self.coordinator.setThermostat(int(temperature))

async def async_set_fan_mode(self, fan_mode):
"""Set the fan mode."""
_LOGGER.debug ('set fan_mode %s', fan_mode)
await self.coordinator.setFlow (int(fan_mode))
_LOGGER.debug("set fan_mode %s", fan_mode)
await self.coordinator.setFlow(int(fan_mode))

async def async_set_hvac_mode(self, hvac_mode):
"""Set new target hvac mode."""
_LOGGER.debug ('set hvac_mode %s', hvac_mode)
_LOGGER.debug("set hvac_mode %s", hvac_mode)
display = await self.coordinator.resetMenu()
current_mode = display.split('/')[0].split(' ')[0]
current_mode = display.split("/")[0].split(" ")[0]
if self._mode_map[current_mode] == hvac_mode:
return
elif hvac_mode == HVACMode.OFF:
await self.coordinator.key_off()
else:
if current_mode == 'OFF':
if current_mode == "OFF":
await self.coordinator.key_on()
await self.coordinator.setMode (self._mode_imap[hvac_mode])
await self.coordinator.setMode(self._mode_imap[hvac_mode])

def turn_on(self) -> None:
"""Turn the entity on."""
self.coordinator.key_on()

async def async_turn_on(self) -> None:
await self.coordinator.key_on()

def turn_off(self) -> None:
"""Turn the entity off."""
self.coordinator.key_off()

async def async_turn_off(self) -> None:
await self.coordinator.key_off()

Loading