Flexit component fix for updated modbus (#53583)
* pyflexit first argument should be a ModbusSerialClient This component broke with 2021.6 I have tested this patch on my setup and it restores functionality * Implemented async reading of modbus values Stopped using pyflexit as this is outdated and not needed Instead using async_pymodbus_call from ModbusHub class * Bugfix: Reading fan mode from wrong register * Implemented async writing Set target temperature and fan mode using modbus call Added some error handling * No longer require pyflexit * Review comments. Co-authored-by: jan Iversen <jancasacondor@gmail.com>
This commit is contained in:
parent
0b52e13eb8
commit
6830eec549
3 changed files with 119 additions and 34 deletions
|
@ -3,7 +3,6 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
|
||||
from pyflexit.pyflexit import pyflexit
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity
|
||||
|
@ -12,7 +11,15 @@ from homeassistant.components.climate.const import (
|
|||
SUPPORT_FAN_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.components.modbus.const import CONF_HUB, DEFAULT_HUB, MODBUS_DOMAIN
|
||||
from homeassistant.components.modbus.const import (
|
||||
CALL_TYPE_REGISTER_HOLDING,
|
||||
CALL_TYPE_REGISTER_INPUT,
|
||||
CALL_TYPE_WRITE_REGISTER,
|
||||
CONF_HUB,
|
||||
DEFAULT_HUB,
|
||||
MODBUS_DOMAIN,
|
||||
)
|
||||
from homeassistant.components.modbus.modbus import ModbusHub
|
||||
from homeassistant.const import (
|
||||
ATTR_TEMPERATURE,
|
||||
CONF_NAME,
|
||||
|
@ -20,7 +27,9 @@ from homeassistant.const import (
|
|||
DEVICE_DEFAULT_NAME,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
|
@ -35,18 +44,25 @@ _LOGGER = logging.getLogger(__name__)
|
|||
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
async_add_entities,
|
||||
discovery_info: DiscoveryInfoType = None,
|
||||
):
|
||||
"""Set up the Flexit Platform."""
|
||||
modbus_slave = config.get(CONF_SLAVE)
|
||||
name = config.get(CONF_NAME)
|
||||
hub = hass.data[MODBUS_DOMAIN][config.get(CONF_HUB)]
|
||||
add_entities([Flexit(hub, modbus_slave, name)], True)
|
||||
async_add_entities([Flexit(hub, modbus_slave, name)], True)
|
||||
|
||||
|
||||
class Flexit(ClimateEntity):
|
||||
"""Representation of a Flexit AC unit."""
|
||||
|
||||
def __init__(self, hub, modbus_slave, name):
|
||||
def __init__(
|
||||
self, hub: ModbusHub, modbus_slave: int | None, name: str | None
|
||||
) -> None:
|
||||
"""Initialize the unit."""
|
||||
self._hub = hub
|
||||
self._name = name
|
||||
|
@ -64,34 +80,65 @@ class Flexit(ClimateEntity):
|
|||
self._heating = None
|
||||
self._cooling = None
|
||||
self._alarm = False
|
||||
self.unit = pyflexit(hub, modbus_slave)
|
||||
self._outdoor_air_temp = None
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Return the list of supported features."""
|
||||
return SUPPORT_FLAGS
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Update unit attributes."""
|
||||
if not self.unit.update():
|
||||
_LOGGER.warning("Modbus read failed")
|
||||
self._target_temperature = await self._async_read_temp_from_register(
|
||||
CALL_TYPE_REGISTER_HOLDING, 8
|
||||
)
|
||||
self._current_temperature = await self._async_read_temp_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 9
|
||||
)
|
||||
res = await self._async_read_int16_from_register(CALL_TYPE_REGISTER_HOLDING, 17)
|
||||
if res < len(self._fan_modes):
|
||||
self._current_fan_mode = res
|
||||
self._filter_hours = await self._async_read_int16_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 8
|
||||
)
|
||||
# # Mechanical heat recovery, 0-100%
|
||||
self._heat_recovery = await self._async_read_int16_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 14
|
||||
)
|
||||
# # Heater active 0-100%
|
||||
self._heating = await self._async_read_int16_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 15
|
||||
)
|
||||
# # Cooling active 0-100%
|
||||
self._cooling = await self._async_read_int16_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 13
|
||||
)
|
||||
# # Filter alarm 0/1
|
||||
self._filter_alarm = await self._async_read_int16_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 27
|
||||
)
|
||||
# # Heater enabled or not. Does not mean it's necessarily heating
|
||||
self._heater_enabled = await self._async_read_int16_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 28
|
||||
)
|
||||
self._outdoor_air_temp = await self._async_read_temp_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 11
|
||||
)
|
||||
|
||||
self._target_temperature = self.unit.get_target_temp
|
||||
self._current_temperature = self.unit.get_temp
|
||||
self._current_fan_mode = self._fan_modes[self.unit.get_fan_speed]
|
||||
self._filter_hours = self.unit.get_filter_hours
|
||||
# Mechanical heat recovery, 0-100%
|
||||
self._heat_recovery = self.unit.get_heat_recovery
|
||||
# Heater active 0-100%
|
||||
self._heating = self.unit.get_heating
|
||||
# Cooling active 0-100%
|
||||
self._cooling = self.unit.get_cooling
|
||||
# Filter alarm 0/1
|
||||
self._filter_alarm = self.unit.get_filter_alarm
|
||||
# Heater enabled or not. Does not mean it's necessarily heating
|
||||
self._heater_enabled = self.unit.get_heater_enabled
|
||||
# Current operation mode
|
||||
self._current_operation = self.unit.get_operation
|
||||
actual_air_speed = await self._async_read_int16_from_register(
|
||||
CALL_TYPE_REGISTER_INPUT, 48
|
||||
)
|
||||
|
||||
if self._heating:
|
||||
self._current_operation = "Heating"
|
||||
elif self._cooling:
|
||||
self._current_operation = "Cooling"
|
||||
elif self._heat_recovery:
|
||||
self._current_operation = "Recovering"
|
||||
elif actual_air_speed:
|
||||
self._current_operation = "Fan Only"
|
||||
else:
|
||||
self._current_operation = "Off"
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
|
@ -103,6 +150,7 @@ class Flexit(ClimateEntity):
|
|||
"heating": self._heating,
|
||||
"heater_enabled": self._heater_enabled,
|
||||
"cooling": self._cooling,
|
||||
"outdoor_air_temp": self._outdoor_air_temp,
|
||||
}
|
||||
|
||||
@property
|
||||
|
@ -153,12 +201,53 @@ class Flexit(ClimateEntity):
|
|||
"""Return the list of available fan modes."""
|
||||
return self._fan_modes
|
||||
|
||||
def set_temperature(self, **kwargs):
|
||||
async def async_set_temperature(self, **kwargs):
|
||||
"""Set new target temperature."""
|
||||
if kwargs.get(ATTR_TEMPERATURE) is not None:
|
||||
self._target_temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
self.unit.set_temp(self._target_temperature)
|
||||
target_temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
else:
|
||||
_LOGGER.error("Received invalid temperature")
|
||||
return
|
||||
|
||||
def set_fan_mode(self, fan_mode):
|
||||
if await self._async_write_int16_to_register(8, target_temperature * 10):
|
||||
self._target_temperature = target_temperature
|
||||
else:
|
||||
_LOGGER.error("Modbus error setting target temperature to Flexit")
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode):
|
||||
"""Set new fan mode."""
|
||||
self.unit.set_fan_speed(self._fan_modes.index(fan_mode))
|
||||
if await self._async_write_int16_to_register(
|
||||
17, self.fan_modes.index(fan_mode)
|
||||
):
|
||||
self._current_fan_mode = self.fan_modes.index(fan_mode)
|
||||
else:
|
||||
_LOGGER.error("Modbus error setting fan mode to Flexit")
|
||||
|
||||
# Based on _async_read_register in ModbusThermostat class
|
||||
async def _async_read_int16_from_register(self, register_type, register) -> int:
|
||||
"""Read register using the Modbus hub slave."""
|
||||
result = await self._hub.async_pymodbus_call(
|
||||
self._slave, register, 1, register_type
|
||||
)
|
||||
if result is None:
|
||||
_LOGGER.error("Error reading value from Flexit modbus adapter")
|
||||
return -1
|
||||
|
||||
return int(result.registers[0])
|
||||
|
||||
async def _async_read_temp_from_register(self, register_type, register) -> float:
|
||||
result = float(
|
||||
await self._async_read_int16_from_register(register_type, register)
|
||||
)
|
||||
if result == -1:
|
||||
return -1
|
||||
return result / 10.0
|
||||
|
||||
async def _async_write_int16_to_register(self, register, value) -> bool:
|
||||
value = int(value)
|
||||
result = await self._hub.async_pymodbus_call(
|
||||
self._slave, register, value, CALL_TYPE_WRITE_REGISTER
|
||||
)
|
||||
if result == -1:
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
"domain": "flexit",
|
||||
"name": "Flexit",
|
||||
"documentation": "https://www.home-assistant.io/integrations/flexit",
|
||||
"requirements": ["pyflexit==0.3"],
|
||||
"dependencies": ["modbus"],
|
||||
"codeowners": [],
|
||||
"iot_class": "local_polling"
|
||||
|
|
|
@ -1437,9 +1437,6 @@ pyfido==2.1.1
|
|||
# homeassistant.components.fireservicerota
|
||||
pyfireservicerota==0.0.43
|
||||
|
||||
# homeassistant.components.flexit
|
||||
pyflexit==0.3
|
||||
|
||||
# homeassistant.components.flic
|
||||
pyflic==2.0.3
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue