Skip to content

Commit

Permalink
New option for climate device to expose temperture sensors (#595)
Browse files Browse the repository at this point in the history
* Expose temperature sensors for climate

* Fix config name

* Cleanup

* Fixes + cleanup

* Added test

* isort fix + changelog

* Rename "expose_sensor" for weather & climate to "create_sensor"

* Changelog adjustment
  • Loading branch information
spacegaier committed Feb 16, 2021
1 parent 7521754 commit afa30c7
Show file tree
Hide file tree
Showing 24 changed files with 102 additions and 39 deletions.
8 changes: 5 additions & 3 deletions changelog.md
Expand Up @@ -5,6 +5,8 @@
### Devices

- BinarySensor: return `None` for `BinarySensor.counter` when context timeout is not used (and don't calculate it)
- Climate: Add `create_temperature_sensors` option to create dedicated sensors for current and target temperature.
- Weather (breaking change!): Renamed `expose_sensors` to `create_sensors` to prevent confusion with the XKNX `expose_sensor` device type.

### Internals

Expand All @@ -21,11 +23,11 @@
- Fan: Add `max_step` attribute which defines the maximum amount of steps. If set, the fan is controlled by steps instead of percentage.
- Fan: Add `group_address_oscillation` and `group_address_oscillation_state` attributes to control the oscillation of a fan.

## 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 @@ -177,7 +179,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
4 changes: 2 additions & 2 deletions docs/weather.md
Expand Up @@ -51,7 +51,7 @@ groups:
group_address_day_night: "7/0/8",
group_address_air_pressure: "7/0/9",
group_address_humidity: "7/0/10",
expose_sensors: True,
create_sensors: True,
sync_state: True,
}
```
Expand All @@ -71,7 +71,7 @@ groups:
- **group_address_day_night** KNX address for reading a day/night object.
- **group_address_air_pressure** KNX address reading current air pressure. **DPT 9.006**
- **group_address_humidity** KNX address for reading current humidity. **DPT 9.007**
- **expose_sensors** If true, also exposes all values as sensors to the xknx device list (useful for home assistant). Default: False
- **create_sensors** If true, also adds sensors for all values to the xknx device list (useful for Home Assistant). Default: False
- **sync_state** Periodically sync the state.
- **device_updated_cb** awaitable callback for each update.

Expand Down
5 changes: 4 additions & 1 deletion home-assistant-plugin/custom_components/xknx/factory.py
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],
create_temperature_sensors=config.get(
ClimateSchema.CONF_CREATE_TEMPERATURE_SENSORS
),
)


Expand Down Expand Up @@ -327,7 +330,7 @@ def _create_weather(knx_module: XKNX, config: ConfigType) -> XknxWeather:
knx_module,
name=config[CONF_NAME],
sync_state=config[WeatherSchema.CONF_SYNC_STATE],
expose_sensors=config[WeatherSchema.CONF_XKNX_EXPOSE_SENSORS],
create_sensors=config[WeatherSchema.CONF_XKNX_CREATE_SENSORS],
group_address_temperature=config[WeatherSchema.CONF_XKNX_TEMPERATURE_ADDRESS],
group_address_brightness_south=config.get(
WeatherSchema.CONF_XKNX_BRIGHTNESS_SOUTH_ADDRESS
Expand Down
8 changes: 6 additions & 2 deletions home-assistant-plugin/custom_components/xknx/schema.py
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_CREATE_TEMPERATURE_SENSORS = "create_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_CREATE_TEMPERATURE_SENSORS, default=False
): cv.boolean,
}
),
)
Expand Down Expand Up @@ -403,7 +407,7 @@ class WeatherSchema:
CONF_XKNX_DAY_NIGHT_ADDRESS = "address_day_night"
CONF_XKNX_AIR_PRESSURE_ADDRESS = "address_air_pressure"
CONF_XKNX_HUMIDITY_ADDRESS = "address_humidity"
CONF_XKNX_EXPOSE_SENSORS = "expose_sensors"
CONF_XKNX_CREATE_SENSORS = "create_sensors"

DEFAULT_NAME = "KNX Weather Station"

Expand All @@ -415,7 +419,7 @@ class WeatherSchema:
cv.boolean,
cv.string,
),
vol.Optional(CONF_XKNX_EXPOSE_SENSORS, default=False): cv.boolean,
vol.Optional(CONF_XKNX_CREATE_SENSORS, default=False): cv.boolean,
vol.Required(CONF_XKNX_TEMPERATURE_ADDRESS): cv.string,
vol.Optional(CONF_XKNX_BRIGHTNESS_SOUTH_ADDRESS): cv.string,
vol.Optional(CONF_XKNX_BRIGHTNESS_EAST_ADDRESS): cv.string,
Expand Down
4 changes: 2 additions & 2 deletions test/config_tests/config_v1_test.py
Expand Up @@ -587,12 +587,12 @@ def test_config_weather(self):
group_address_day_night="7/0/8",
group_address_air_pressure="7/0/9",
group_address_humidity="7/0/10",
expose_sensors=False,
create_sensors=False,
sync_state=True,
),
)

def test_config_weather_expose_sensor(self):
def test_config_weather_create_sensor(self):
"""Test reading weather from config file."""
self.assertTrue(isinstance(TestConfig.xknx.devices["Home_temperature"], Sensor))
self.assertTrue(
Expand Down
2 changes: 1 addition & 1 deletion test/config_tests/resources/weather/invalid_1.yaml
Expand Up @@ -33,4 +33,4 @@ air_pressure:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
2 changes: 1 addition & 1 deletion test/config_tests/resources/weather/invalid_2.yaml
Expand Up @@ -34,4 +34,4 @@ air_pressure:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
2 changes: 1 addition & 1 deletion test/config_tests/resources/weather/valid_1.yaml
Expand Up @@ -36,4 +36,4 @@ air_pressure:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
2 changes: 1 addition & 1 deletion test/config_tests/resources/weather/valid_2.yaml
Expand Up @@ -3,4 +3,4 @@ friendly_name: "Home"
temperature:
state_address: "7/0/0"
state_update: "expire 60"
expose_sensors: "off"
create_sensors: "off"
2 changes: 1 addition & 1 deletion test/config_tests/resources/xknx/invalid_1.yaml
Expand Up @@ -157,7 +157,7 @@ weather:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
datetime:
- name: "Generaltime"
time:
Expand Down
2 changes: 1 addition & 1 deletion test/config_tests/resources/xknx/invalid_2.yaml
Expand Up @@ -155,7 +155,7 @@ weather:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
datetime:
- name: "Generaltime"
time:
Expand Down
2 changes: 1 addition & 1 deletion test/config_tests/resources/xknx/invalid_3.yaml
Expand Up @@ -154,7 +154,7 @@ weather:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
datetime:
- name: "Generaltime"
time:
Expand Down
2 changes: 1 addition & 1 deletion test/config_tests/resources/xknx/valid_1.yaml
Expand Up @@ -160,7 +160,7 @@ weather:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
datetime:
- name: "Generaltime"
time:
Expand Down
2 changes: 1 addition & 1 deletion test/config_tests/resources/xknx/valid_2.yaml
Expand Up @@ -157,7 +157,7 @@ weather:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
datetime:
- name: "Generaltime"
time:
Expand Down
2 changes: 1 addition & 1 deletion test/config_tests/resources/xknx/valid_3.yaml
Expand Up @@ -162,7 +162,7 @@ weather:
humidity:
state_address: "7/0/10"
state_update: "expire 60"
expose_sensors: True
create_sensors: True
datetime:
- name: "Generaltime"
time:
Expand Down
25 changes: 25 additions & 0 deletions test/devices_tests/climate_test.py
Expand Up @@ -1456,3 +1456,28 @@ def test_power_on_off(self):
payload=GroupValueWrite(DPTBinary(True)),
),
)

#
# Create temperature sensor tests
#
def test_create_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",
create_temperature_sensors=True,
)

self.assertEqual(len(xknx.devices), 3)
6 changes: 3 additions & 3 deletions test/devices_tests/weather_test.py
Expand Up @@ -291,9 +291,9 @@ def test_weather_default(self):
self.assertEqual(weather.ha_current_state(), WeatherCondition.exceptional)

#
# Expose Sensor tests
# Create sensor tests
#
def test_expose_sensor(self):
def test_create_sensor(self):
"""Test default state mapping."""
xknx = XKNX()
Weather(
Expand All @@ -313,7 +313,7 @@ def test_expose_sensor(self):
group_address_brightness_south="1/3/6",
group_address_brightness_west="1/3/7",
group_address_wind_alarm="1/5/4",
expose_sensors=True,
create_sensors=True,
)

self.assertEqual(len(xknx.devices), 6)
Expand Down
2 changes: 1 addition & 1 deletion test/str_test.py
Expand Up @@ -339,7 +339,7 @@ def test_weather(self):
group_address_day_night="7/0/7",
group_address_rain_alarm="7/0/0",
group_address_frost_alarm="7/0/8",
expose_sensors=True,
create_sensors=True,
group_address_air_pressure="7/0/9",
group_address_humidity="7/0/9",
group_address_wind_alarm="7/0/10",
Expand Down
4 changes: 2 additions & 2 deletions xknx.yaml
Expand Up @@ -288,7 +288,7 @@ groups:
group_address_day_night: "7/0/8",
group_address_air_pressure: "7/0/9",
group_address_humidity: "7/0/10",
expose_sensors: True,
create_sensors: True,
sync_state: True,
}
Remote:
Expand All @@ -304,6 +304,6 @@ groups:
group_address_day_night: "7/0/8",
group_address_air_pressure: "7/0/9",
group_address_humidity: "7/0/10",
expose_sensors: False,
create_sensors: False,
sync_state: True,
}
6 changes: 4 additions & 2 deletions xknx/config/schema.py
Expand Up @@ -323,6 +323,7 @@ class ClimateSchema:
CONF_HEAT_COOL = "heat_cool"
CONF_OPERATION_MODES = "operation_modes"
CONF_ON_OFF = "on_off"
CONF_CREATE_TEMPERATURE_SENSORS = "create_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_CREATE_TEMPERATURE_SENSORS, default=False): boolean,
}
)

Expand All @@ -435,7 +437,7 @@ class WeatherSchema:
CONF_DAY_NIGHT = "day_night"
CONF_AIR_PRESSURE = "air_pressure"
CONF_HUMIDITY = "humidity"
CONF_EXPOSE_SENSORS = "expose_sensors"
CONF_CREATE_SENSORS = "create_sensors"

SCHEMA = BaseDeviceSchema.SCHEMA.extend(
{
Expand Down Expand Up @@ -511,7 +513,7 @@ class WeatherSchema:
vol.Required(CONF_STATE_ADDRESS): ensure_group_address,
}
),
vol.Optional(CONF_EXPOSE_SENSORS, default=False): boolean,
vol.Optional(CONF_CREATE_SENSORS, default=False): boolean,
}
)

Expand Down
29 changes: 28 additions & 1 deletion xknx/devices/climate.py
Expand Up @@ -16,6 +16,7 @@

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

if TYPE_CHECKING:
from xknx.remote_value import RemoteValue
Expand Down Expand Up @@ -62,6 +63,7 @@ def __init__(
min_temp: Optional[float] = None,
max_temp: Optional[float] = None,
mode: Optional[ClimateMode] = None,
create_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 create_temperature_sensors:
self.create_temperature_sensors()

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

def create_temperature_sensors(self) -> None:
"""Create temperature sensors."""
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

0 comments on commit afa30c7

Please sign in to comment.