Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New option for climate device to expose temperture sensors #595

Merged
merged 10 commits into from
Feb 16, 2021
7 changes: 4 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
### Devices

- Fan: Add `max_step` attribute which defines the maximum amount of steps. If set, the fan is controlled by steps instead of percentage.
- Climate: Add option to create dedicated sensors for current and target temperature.

## 0.16.2 Bugfix for yaml loader 2021-01-24
## 0.16.2 Bugfix for YAML loader 2021-01-24

### Internals

- fix conflict with HA Yaml loader
- fix conflict with HA YAML loader

## 0.16.1 HA register services 2021-01-16

Expand Down Expand Up @@ -162,7 +163,7 @@
- Reset binary sensor counters after the context has been timed out in order to be able to use state change events within HA
- Code cleanups

## 0.14.0 New sensor types and refacoring of binary sensor automations
## 0.14.0 New sensor types and refactoring of binary sensor automations

### Breaking changes

Expand Down
3 changes: 3 additions & 0 deletions home-assistant-plugin/custom_components/xknx/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,9 @@ def _create_climate(knx_module: XKNX, config: ConfigType) -> XknxClimate:
max_temp=config.get(ClimateSchema.CONF_MAX_TEMP),
mode=climate_mode,
on_off_invert=config[ClimateSchema.CONF_ON_OFF_INVERT],
expose_temperature_sensors=config.get(
ClimateSchema.CONF_EXPOSE_TEMPERATURE_SENSORS
),
)


Expand Down
4 changes: 4 additions & 0 deletions home-assistant-plugin/custom_components/xknx/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ class ClimateSchema:
CONF_ON_OFF_INVERT = "on_off_invert"
CONF_MIN_TEMP = "min_temp"
CONF_MAX_TEMP = "max_temp"
CONF_EXPOSE_TEMPERATURE_SENSORS = "expose_temperature_sensors"

DEFAULT_NAME = "KNX Climate"
DEFAULT_SETPOINT_SHIFT_MODE = "DPT6010"
Expand Down Expand Up @@ -295,6 +296,9 @@ class ClimateSchema:
),
vol.Optional(CONF_MIN_TEMP): vol.Coerce(float),
vol.Optional(CONF_MAX_TEMP): vol.Coerce(float),
vol.Optional(
CONF_EXPOSE_TEMPERATURE_SENSORS, default=False
): cv.boolean,
}
),
)
Expand Down
25 changes: 25 additions & 0 deletions test/devices_tests/climate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1456,3 +1456,28 @@ def test_power_on_off(self):
payload=GroupValueWrite(DPTBinary(True)),
),
)

#
# Expose temperature sensor tests
#
def test_expose_sensor(self):
"""Test default state mapping."""
xknx = XKNX()
Climate(
name="climate",
xknx=xknx,
group_address_temperature="5/1/1",
group_address_target_temperature="5/1/4",
)

self.assertEqual(len(xknx.devices), 1)

Climate(
name="climate",
xknx=xknx,
group_address_temperature="5/1/1",
group_address_target_temperature="5/1/4",
expose_temperature_sensors=True,
)

self.assertEqual(len(xknx.devices), 3)
2 changes: 2 additions & 0 deletions xknx/config/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ class ClimateSchema:
CONF_HEAT_COOL = "heat_cool"
CONF_OPERATION_MODES = "operation_modes"
CONF_ON_OFF = "on_off"
CONF_EXPOSE_TEMPERATURE_SENSORS = "expose_temperature_sensors"

CONF_FROST_PROTECTION_ADDRESS = "frost_protection_address"
CONF_NIGHT_ADDRESS = "night_address"
Expand Down Expand Up @@ -416,6 +417,7 @@ class ClimateSchema:
vol.Optional(CONF_OPERATION_MODES): vol.All(
ensure_list, [vol.In({**OPERATION_MODES, **PRESET_MODES})]
),
vol.Optional(CONF_EXPOSE_TEMPERATURE_SENSORS, default=False): boolean,
}
)

Expand Down
29 changes: 28 additions & 1 deletion xknx/devices/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
RemoteValueTemp,
)

from .sensor import Sensor
from .climate_mode import ClimateMode
from .device import Device, DeviceCallbackType

Expand Down Expand Up @@ -62,6 +63,7 @@ def __init__(
min_temp: Optional[float] = None,
max_temp: Optional[float] = None,
mode: Optional[ClimateMode] = None,
expose_temperature_sensors: bool = False,
device_updated_cb: Optional[DeviceCallbackType] = None,
):
"""Initialize Climate class."""
Expand All @@ -78,7 +80,7 @@ def __init__(
xknx,
group_address_state=group_address_temperature,
device_name=self.name,
feature_name="Current Temperature",
feature_name="Current temperature",
after_update_cb=self.after_update,
)

Expand Down Expand Up @@ -125,6 +127,9 @@ def __init__(

self.mode = mode

if expose_temperature_sensors:
self.expose_temperature_sensors()

def _iter_remote_values(self) -> Iterator["RemoteValue"]:
"""Iterate the devices RemoteValue classes."""
yield from (
Expand All @@ -134,6 +139,28 @@ def _iter_remote_values(self) -> Iterator["RemoteValue"]:
self.on,
)

def expose_temperature_sensors(self) -> None:
"""Expose temperature sensors to xknx."""
for suffix, group_address, value_type in (
(
"temperature",
self.temperature.group_address_state,
"temperature",
),
(
"target temperature",
self.target_temperature.group_address_state,
"temperature",
),
):
if group_address is not None:
Sensor(
self.xknx,
name=self.name + " " + suffix,
group_address_state=group_address,
value_type=value_type,
)

@classmethod
def from_config(cls, xknx: "XKNX", name: str, config: Any) -> "Climate":
"""Initialize object from configuration structure."""
Expand Down
6 changes: 3 additions & 3 deletions xknx/remote_value/remote_value_climate_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def __init__(
group_address_state: Optional["GroupAddressableType"] = None,
sync_state: bool = True,
device_name: Optional[str] = None,
feature_name: str = "Climate Mode",
feature_name: str = "Climate mode",
climate_mode_type: Optional[ClimateModeType] = None,
after_update_cb: Optional["AsyncCallback"] = None,
passive_group_addresses: Optional[List["GroupAddressableType"]] = None,
Expand Down Expand Up @@ -125,7 +125,7 @@ def __init__(
group_address_state: Optional["GroupAddressableType"] = None,
sync_state: bool = True,
device_name: Optional[str] = None,
feature_name: str = "Climate Mode Binary",
feature_name: str = "Climate mode binary",
after_update_cb: Optional["AsyncCallback"] = None,
operation_mode: Optional[HVACOperationMode] = None,
):
Expand Down Expand Up @@ -207,7 +207,7 @@ def __init__(
group_address_state: Optional["GroupAddressableType"] = None,
sync_state: bool = True,
device_name: Optional[str] = None,
feature_name: str = "Controller Mode Heat/Cool",
feature_name: str = "Controller mode Heat/Cool",
after_update_cb: Optional["AsyncCallback"] = None,
controller_mode: Optional[HVACControllerMode] = None,
):
Expand Down