-
-
Notifications
You must be signed in to change notification settings - Fork 37.5k
Add number platform to eurotronic_cometblue #168119
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
Changes from all commits
ad7ebfc
3991b4a
7167199
93927df
aec58e4
13a62f8
daeb65a
8c9d97a
9a82f26
86b4e53
f3085c2
2584bfb
a72e659
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| """Comet Blue number integration.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from collections.abc import Callable | ||
| from dataclasses import dataclass | ||
| from typing import Any | ||
|
|
||
| from eurotronic_cometblue_ha import AsyncCometBlue | ||
|
|
||
| from homeassistant.components.number import ( | ||
| NumberDeviceClass, | ||
| NumberEntity, | ||
| NumberEntityDescription, | ||
| ) | ||
| from homeassistant.const import PRECISION_HALVES, EntityCategory, UnitOfTemperature | ||
| from homeassistant.core import HomeAssistant | ||
| from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback | ||
|
|
||
| from .climate import MAX_TEMP, MIN_TEMP | ||
| from .coordinator import CometBlueConfigEntry, CometBlueDataUpdateCoordinator | ||
| from .entity import CometBlueBluetoothEntity | ||
|
|
||
| PARALLEL_UPDATES = 1 | ||
|
|
||
|
|
||
| @dataclass(frozen=True, kw_only=True) | ||
| class CometBlueRequiredKeysMixin: | ||
| """Mixin for required keys.""" | ||
|
|
||
| cometblue_key: str | ||
| set_fn: Callable[[AsyncCometBlue], Any] | ||
|
|
||
|
|
||
| @dataclass(frozen=True, kw_only=True) | ||
| class CometBlueNumberEntityDescription( | ||
| NumberEntityDescription, CometBlueRequiredKeysMixin | ||
| ): | ||
| """Describes a Comet Blue number entity.""" | ||
|
|
||
|
|
||
| DESCRIPTIONS = [ | ||
| CometBlueNumberEntityDescription( | ||
| key="offset", | ||
| cometblue_key="tempOffset", | ||
| translation_key="offset", | ||
| device_class=NumberDeviceClass.TEMPERATURE, | ||
| entity_category=EntityCategory.CONFIG, | ||
| native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||
| set_fn=lambda x: x.set_temperature_async, | ||
| native_min_value=-5.0, | ||
| native_max_value=5.0, | ||
| native_step=PRECISION_HALVES, | ||
| entity_registry_enabled_default=False, | ||
| ), | ||
|
rikroe marked this conversation as resolved.
|
||
| CometBlueNumberEntityDescription( | ||
| key="eco_setpoint", | ||
| cometblue_key="targetTempLow", | ||
| translation_key="eco_setpoint", | ||
| device_class=NumberDeviceClass.TEMPERATURE, | ||
| entity_category=EntityCategory.CONFIG, | ||
| native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||
| set_fn=lambda x: x.set_temperature_async, | ||
| native_min_value=MIN_TEMP, | ||
| native_max_value=MAX_TEMP, | ||
| native_step=PRECISION_HALVES, | ||
| entity_registry_enabled_default=True, | ||
| ), | ||
| CometBlueNumberEntityDescription( | ||
| key="comfort_setpoint", | ||
| cometblue_key="targetTempHigh", | ||
| translation_key="comfort_setpoint", | ||
| device_class=NumberDeviceClass.TEMPERATURE, | ||
| entity_category=EntityCategory.CONFIG, | ||
| native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||
| set_fn=lambda x: x.set_temperature_async, | ||
| native_min_value=MIN_TEMP, | ||
| native_max_value=MAX_TEMP, | ||
| native_step=PRECISION_HALVES, | ||
| entity_registry_enabled_default=True, | ||
| ), | ||
|
Comment on lines
+56
to
+81
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this part of the climate entity?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, its part of the climate entity (as attribute). But it is not possible to set the values, as only the one target setpoint can be changed. To adjust this, we need the number entity.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you set the hvac mode to heat cool you can set that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well the device doesn't support cooling (only not heating). And hvacmode heat is mapped to a boost temperature/fully open valve because that is the only setting where the device doesn't regulate itself. As the attributes in the climate platform dont really do anything, should they be removed? If so, in this or a separate PR (I guess the latter)?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, interesting, I'd assume that the range would still work as there's a supported feature for it, so I think it's set correctly, it's now more how the frontend can update it. Is this something we should take to some frontenders? We could consider splitting it off from this PR so we don't have to deprecate it if we find a solution and this has been released already
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, based on your persistent feedback I double checked the actual service call ( In this case, yes, I'll remove it from here (and open another PR to get it into the climate component). I'll ask in the frontend discord if I'm missing something.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like I know heat_cool has that double slider setup, but I'd expect that to happen to other ones as well when temperature_range is set as supported
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remembered why I implemented it like this. Its either possible to set the target temperature OR the high/low temperatures via UI, depending on the capabilities. If high/low is set via UI, then the current target temperature cannot be set/overriden, as high/low only apply with the next schedule change. Therefore I added the number entities (same as the on-device schedule which would need an entity action to be set). As target high/low temperatures can still be set via the device action (or rather, can be set after an additional, small PR), I'm indifferent if these should be added as separate number/configuration entity. Please just let me know what you prefer. More details: If a device supports BOTH If a device supports ONLY
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looked at other climate platforms that support So probably the Eurotronic CometBlue climate platform should not expose it (i.e. set only
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With #169182, we should keep the number entities here. Also renamed the temperatures to setpoints to make it clearer. |
||
| ] | ||
|
|
||
|
|
||
| async def async_setup_entry( | ||
| hass: HomeAssistant, | ||
| entry: CometBlueConfigEntry, | ||
| async_add_entities: AddConfigEntryEntitiesCallback, | ||
| ) -> None: | ||
| """Set up the client entities.""" | ||
|
|
||
| coordinator = entry.runtime_data | ||
| entities: list[CometBlueNumberEntity] = [ | ||
| CometBlueNumberEntity(coordinator, description) for description in DESCRIPTIONS | ||
| ] | ||
|
|
||
| async_add_entities(entities) | ||
|
|
||
|
|
||
| class CometBlueNumberEntity(CometBlueBluetoothEntity, NumberEntity): | ||
| """Representation of a number.""" | ||
|
|
||
| entity_description: CometBlueNumberEntityDescription | ||
|
|
||
| def __init__( | ||
| self, | ||
| coordinator: CometBlueDataUpdateCoordinator, | ||
| description: CometBlueNumberEntityDescription, | ||
| ) -> None: | ||
| """Initialize CometBlueNumberEntity.""" | ||
|
|
||
| super().__init__(coordinator) | ||
| self.entity_description = description | ||
| self._attr_unique_id = f"{coordinator.address}-{description.key}" | ||
|
|
||
| @property | ||
| def native_value(self) -> float | None: | ||
| """Return the entity value to represent the entity state.""" | ||
| return self.coordinator.data.temperatures.get( | ||
| self.entity_description.cometblue_key | ||
| ) | ||
|
|
||
| async def async_set_native_value(self, value: float) -> None: | ||
| """Update to the device.""" | ||
|
|
||
| await self.coordinator.send_command( | ||
| self.entity_description.set_fn(self.coordinator.device), | ||
| { | ||
| "values": { | ||
| # manual temperature always needs to be set, otherwise TRV will turn OFF | ||
| "manualTemp": self.coordinator.data.temperatures["manualTemp"], | ||
| self.entity_description.cometblue_key: value, | ||
| } | ||
|
rikroe marked this conversation as resolved.
|
||
| }, | ||
|
rikroe marked this conversation as resolved.
|
||
| ) | ||
|
rikroe marked this conversation as resolved.
rikroe marked this conversation as resolved.
|
||
| await self.coordinator.async_request_refresh() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| # serializer version: 1 | ||
| # name: test_number_state[number.comet_blue_aa_bb_cc_dd_ee_ff_comfort_setpoint-entry] | ||
| EntityRegistryEntrySnapshot({ | ||
| 'aliases': list([ | ||
| None, | ||
| ]), | ||
| 'area_id': None, | ||
| 'capabilities': dict({ | ||
| 'max': 28.5, | ||
| 'min': 7.5, | ||
| 'mode': <NumberMode.AUTO: 'auto'>, | ||
| 'step': 0.5, | ||
| }), | ||
| 'config_entry_id': <ANY>, | ||
| 'config_subentry_id': <ANY>, | ||
| 'device_class': None, | ||
| 'device_id': <ANY>, | ||
| 'disabled_by': None, | ||
| 'domain': 'number', | ||
| 'entity_category': <EntityCategory.CONFIG: 'config'>, | ||
| 'entity_id': 'number.comet_blue_aa_bb_cc_dd_ee_ff_comfort_setpoint', | ||
| 'has_entity_name': True, | ||
| 'hidden_by': None, | ||
| 'icon': None, | ||
| 'id': <ANY>, | ||
| 'labels': set({ | ||
| }), | ||
| 'name': None, | ||
| 'object_id_base': 'Comfort setpoint', | ||
| 'options': dict({ | ||
| }), | ||
| 'original_device_class': <NumberDeviceClass.TEMPERATURE: 'temperature'>, | ||
| 'original_icon': None, | ||
| 'original_name': 'Comfort setpoint', | ||
| 'platform': 'eurotronic_cometblue', | ||
| 'previous_unique_id': None, | ||
| 'suggested_object_id': None, | ||
| 'supported_features': 0, | ||
| 'translation_key': 'comfort_setpoint', | ||
| 'unique_id': 'aa:bb:cc:dd:ee:ff-comfort_setpoint', | ||
| 'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>, | ||
| }) | ||
| # --- | ||
| # name: test_number_state[number.comet_blue_aa_bb_cc_dd_ee_ff_comfort_setpoint-state] | ||
| StateSnapshot({ | ||
| 'attributes': ReadOnlyDict({ | ||
| 'device_class': 'temperature', | ||
| 'friendly_name': 'Comet Blue aa:bb:cc:dd:ee:ff Comfort setpoint', | ||
| 'max': 28.5, | ||
| 'min': 7.5, | ||
| 'mode': <NumberMode.AUTO: 'auto'>, | ||
| 'step': 0.5, | ||
| 'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>, | ||
| }), | ||
| 'context': <ANY>, | ||
| 'entity_id': 'number.comet_blue_aa_bb_cc_dd_ee_ff_comfort_setpoint', | ||
| 'last_changed': <ANY>, | ||
| 'last_reported': <ANY>, | ||
| 'last_updated': <ANY>, | ||
| 'state': '21.0', | ||
| }) | ||
| # --- | ||
| # name: test_number_state[number.comet_blue_aa_bb_cc_dd_ee_ff_eco_setpoint-entry] | ||
| EntityRegistryEntrySnapshot({ | ||
| 'aliases': list([ | ||
| None, | ||
| ]), | ||
| 'area_id': None, | ||
| 'capabilities': dict({ | ||
| 'max': 28.5, | ||
| 'min': 7.5, | ||
| 'mode': <NumberMode.AUTO: 'auto'>, | ||
| 'step': 0.5, | ||
| }), | ||
| 'config_entry_id': <ANY>, | ||
| 'config_subentry_id': <ANY>, | ||
| 'device_class': None, | ||
| 'device_id': <ANY>, | ||
| 'disabled_by': None, | ||
| 'domain': 'number', | ||
| 'entity_category': <EntityCategory.CONFIG: 'config'>, | ||
| 'entity_id': 'number.comet_blue_aa_bb_cc_dd_ee_ff_eco_setpoint', | ||
| 'has_entity_name': True, | ||
| 'hidden_by': None, | ||
| 'icon': None, | ||
| 'id': <ANY>, | ||
| 'labels': set({ | ||
| }), | ||
| 'name': None, | ||
| 'object_id_base': 'Eco setpoint', | ||
| 'options': dict({ | ||
| }), | ||
| 'original_device_class': <NumberDeviceClass.TEMPERATURE: 'temperature'>, | ||
| 'original_icon': None, | ||
| 'original_name': 'Eco setpoint', | ||
| 'platform': 'eurotronic_cometblue', | ||
| 'previous_unique_id': None, | ||
| 'suggested_object_id': None, | ||
| 'supported_features': 0, | ||
| 'translation_key': 'eco_setpoint', | ||
| 'unique_id': 'aa:bb:cc:dd:ee:ff-eco_setpoint', | ||
| 'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>, | ||
| }) | ||
| # --- | ||
| # name: test_number_state[number.comet_blue_aa_bb_cc_dd_ee_ff_eco_setpoint-state] | ||
| StateSnapshot({ | ||
| 'attributes': ReadOnlyDict({ | ||
| 'device_class': 'temperature', | ||
| 'friendly_name': 'Comet Blue aa:bb:cc:dd:ee:ff Eco setpoint', | ||
| 'max': 28.5, | ||
| 'min': 7.5, | ||
| 'mode': <NumberMode.AUTO: 'auto'>, | ||
| 'step': 0.5, | ||
| 'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>, | ||
| }), | ||
| 'context': <ANY>, | ||
| 'entity_id': 'number.comet_blue_aa_bb_cc_dd_ee_ff_eco_setpoint', | ||
| 'last_changed': <ANY>, | ||
| 'last_reported': <ANY>, | ||
| 'last_updated': <ANY>, | ||
| 'state': '17.0', | ||
| }) | ||
| # --- | ||
| # name: test_number_state[number.comet_blue_aa_bb_cc_dd_ee_ff_setpoint_offset-entry] | ||
| EntityRegistryEntrySnapshot({ | ||
| 'aliases': list([ | ||
| None, | ||
| ]), | ||
| 'area_id': None, | ||
| 'capabilities': dict({ | ||
| 'max': 5.0, | ||
| 'min': -5.0, | ||
| 'mode': <NumberMode.AUTO: 'auto'>, | ||
| 'step': 0.5, | ||
| }), | ||
| 'config_entry_id': <ANY>, | ||
| 'config_subentry_id': <ANY>, | ||
| 'device_class': None, | ||
| 'device_id': <ANY>, | ||
| 'disabled_by': None, | ||
| 'domain': 'number', | ||
| 'entity_category': <EntityCategory.CONFIG: 'config'>, | ||
| 'entity_id': 'number.comet_blue_aa_bb_cc_dd_ee_ff_setpoint_offset', | ||
| 'has_entity_name': True, | ||
| 'hidden_by': None, | ||
| 'icon': None, | ||
| 'id': <ANY>, | ||
| 'labels': set({ | ||
| }), | ||
| 'name': None, | ||
| 'object_id_base': 'Setpoint offset', | ||
| 'options': dict({ | ||
| }), | ||
| 'original_device_class': <NumberDeviceClass.TEMPERATURE: 'temperature'>, | ||
| 'original_icon': None, | ||
| 'original_name': 'Setpoint offset', | ||
| 'platform': 'eurotronic_cometblue', | ||
| 'previous_unique_id': None, | ||
| 'suggested_object_id': None, | ||
| 'supported_features': 0, | ||
| 'translation_key': 'offset', | ||
| 'unique_id': 'aa:bb:cc:dd:ee:ff-offset', | ||
| 'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>, | ||
| }) | ||
| # --- | ||
| # name: test_number_state[number.comet_blue_aa_bb_cc_dd_ee_ff_setpoint_offset-state] | ||
| StateSnapshot({ | ||
| 'attributes': ReadOnlyDict({ | ||
| 'device_class': 'temperature', | ||
| 'friendly_name': 'Comet Blue aa:bb:cc:dd:ee:ff Setpoint offset', | ||
| 'max': 5.0, | ||
| 'min': -5.0, | ||
| 'mode': <NumberMode.AUTO: 'auto'>, | ||
| 'step': 0.5, | ||
| 'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>, | ||
| }), | ||
| 'context': <ANY>, | ||
| 'entity_id': 'number.comet_blue_aa_bb_cc_dd_ee_ff_setpoint_offset', | ||
| 'last_changed': <ANY>, | ||
| 'last_reported': <ANY>, | ||
| 'last_updated': <ANY>, | ||
| 'state': '0.0', | ||
| }) | ||
| # --- |


Uh oh!
There was an error while loading. Please reload this page.