aiohomematic 2025.10.1__py3-none-any.whl → 2025.10.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aiohomematic might be problematic. Click here for more details.
- aiohomematic/async_support.py +7 -7
- aiohomematic/caches/dynamic.py +31 -26
- aiohomematic/caches/persistent.py +34 -32
- aiohomematic/caches/visibility.py +19 -7
- aiohomematic/central/__init__.py +87 -74
- aiohomematic/central/decorators.py +2 -2
- aiohomematic/central/xml_rpc_server.py +27 -24
- aiohomematic/client/__init__.py +72 -56
- aiohomematic/client/_rpc_errors.py +3 -3
- aiohomematic/client/json_rpc.py +33 -25
- aiohomematic/client/xml_rpc.py +14 -9
- aiohomematic/const.py +2 -1
- aiohomematic/converter.py +19 -19
- aiohomematic/exceptions.py +2 -1
- aiohomematic/model/__init__.py +4 -3
- aiohomematic/model/calculated/__init__.py +1 -1
- aiohomematic/model/calculated/climate.py +9 -9
- aiohomematic/model/calculated/data_point.py +13 -7
- aiohomematic/model/calculated/operating_voltage_level.py +2 -2
- aiohomematic/model/calculated/support.py +7 -7
- aiohomematic/model/custom/__init__.py +3 -3
- aiohomematic/model/custom/climate.py +57 -34
- aiohomematic/model/custom/cover.py +32 -18
- aiohomematic/model/custom/data_point.py +9 -7
- aiohomematic/model/custom/definition.py +23 -17
- aiohomematic/model/custom/light.py +52 -23
- aiohomematic/model/custom/lock.py +16 -12
- aiohomematic/model/custom/siren.py +6 -3
- aiohomematic/model/custom/switch.py +3 -2
- aiohomematic/model/custom/valve.py +3 -2
- aiohomematic/model/data_point.py +62 -49
- aiohomematic/model/device.py +48 -42
- aiohomematic/model/event.py +6 -5
- aiohomematic/model/generic/__init__.py +6 -4
- aiohomematic/model/generic/action.py +1 -1
- aiohomematic/model/generic/data_point.py +7 -5
- aiohomematic/model/generic/number.py +3 -3
- aiohomematic/model/generic/select.py +1 -1
- aiohomematic/model/generic/sensor.py +2 -2
- aiohomematic/model/generic/switch.py +3 -3
- aiohomematic/model/hub/__init__.py +17 -16
- aiohomematic/model/hub/data_point.py +12 -7
- aiohomematic/model/hub/number.py +3 -3
- aiohomematic/model/hub/select.py +3 -3
- aiohomematic/model/hub/text.py +2 -2
- aiohomematic/model/support.py +8 -7
- aiohomematic/model/update.py +6 -6
- aiohomematic/support.py +44 -38
- aiohomematic/validator.py +6 -6
- {aiohomematic-2025.10.1.dist-info → aiohomematic-2025.10.2.dist-info}/METADATA +1 -1
- aiohomematic-2025.10.2.dist-info/RECORD +78 -0
- aiohomematic_support/client_local.py +19 -12
- aiohomematic-2025.10.1.dist-info/RECORD +0 -78
- {aiohomematic-2025.10.1.dist-info → aiohomematic-2025.10.2.dist-info}/WHEEL +0 -0
- {aiohomematic-2025.10.1.dist-info → aiohomematic-2025.10.2.dist-info}/licenses/LICENSE +0 -0
- {aiohomematic-2025.10.1.dist-info → aiohomematic-2025.10.2.dist-info}/top_level.txt +0 -0
|
@@ -19,7 +19,7 @@ from aiohomematic.support import extract_exc_args
|
|
|
19
19
|
_LOGGER: Final = logging.getLogger(__name__)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def _calculate_heat_index(temperature: float, humidity: int) -> float:
|
|
22
|
+
def _calculate_heat_index(*, temperature: float, humidity: int) -> float:
|
|
23
23
|
"""
|
|
24
24
|
Calculate the Heat Index (feels like temperature) based on the NOAA equation.
|
|
25
25
|
|
|
@@ -68,7 +68,7 @@ def _calculate_heat_index(temperature: float, humidity: int) -> float:
|
|
|
68
68
|
return heat_index_celsius
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
def _calculate_wind_chill(temperature: float, wind_speed: float) -> float | None:
|
|
71
|
+
def _calculate_wind_chill(*, temperature: float, wind_speed: float) -> float | None:
|
|
72
72
|
"""
|
|
73
73
|
Calculate the Wind Chill (feels like temperature) based on NOAA.
|
|
74
74
|
|
|
@@ -84,7 +84,7 @@ def _calculate_wind_chill(temperature: float, wind_speed: float) -> float | None
|
|
|
84
84
|
return float(13.12 + (0.6215 * temperature) - 11.37 * wind_speed**0.16 + 0.3965 * temperature * wind_speed**0.16)
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
def calculate_vapor_concentration(temperature: float, humidity: int) -> float | None:
|
|
87
|
+
def calculate_vapor_concentration(*, temperature: float, humidity: int) -> float | None:
|
|
88
88
|
"""Calculate the vapor concentration."""
|
|
89
89
|
try:
|
|
90
90
|
abs_temperature = temperature + 273.15
|
|
@@ -105,7 +105,7 @@ def calculate_vapor_concentration(temperature: float, humidity: int) -> float |
|
|
|
105
105
|
return None
|
|
106
106
|
|
|
107
107
|
|
|
108
|
-
def calculate_apparent_temperature(temperature: float, humidity: int, wind_speed: float) -> float | None:
|
|
108
|
+
def calculate_apparent_temperature(*, temperature: float, humidity: int, wind_speed: float) -> float | None:
|
|
109
109
|
"""Calculate the apparent temperature based on NOAA."""
|
|
110
110
|
try:
|
|
111
111
|
if temperature <= 10 and wind_speed > 4.8:
|
|
@@ -130,7 +130,7 @@ def calculate_apparent_temperature(temperature: float, humidity: int, wind_speed
|
|
|
130
130
|
return None
|
|
131
131
|
|
|
132
132
|
|
|
133
|
-
def calculate_dew_point(temperature: float, humidity: int) -> float | None:
|
|
133
|
+
def calculate_dew_point(*, temperature: float, humidity: int) -> float | None:
|
|
134
134
|
"""Calculate the dew point."""
|
|
135
135
|
try:
|
|
136
136
|
a0 = 373.15 / (273.15 + temperature)
|
|
@@ -155,7 +155,7 @@ def calculate_dew_point(temperature: float, humidity: int) -> float | None:
|
|
|
155
155
|
return None
|
|
156
156
|
|
|
157
157
|
|
|
158
|
-
def calculate_frost_point(temperature: float, humidity: int) -> float | None:
|
|
158
|
+
def calculate_frost_point(*, temperature: float, humidity: int) -> float | None:
|
|
159
159
|
"""Calculate the frost point."""
|
|
160
160
|
try:
|
|
161
161
|
if (dewpoint := calculate_dew_point(temperature=temperature, humidity=humidity)) is None:
|
|
@@ -177,7 +177,7 @@ def calculate_frost_point(temperature: float, humidity: int) -> float | None:
|
|
|
177
177
|
|
|
178
178
|
|
|
179
179
|
def calculate_operating_voltage_level(
|
|
180
|
-
operating_voltage: float | None, low_bat_limit: float | None, voltage_max: float | None
|
|
180
|
+
*, operating_voltage: float | None, low_bat_limit: float | None, voltage_max: float | None
|
|
181
181
|
) -> float | None:
|
|
182
182
|
"""Return the operating voltage level."""
|
|
183
183
|
if operating_voltage is None or low_bat_limit is None or voltage_max is None:
|
|
@@ -152,7 +152,7 @@ _LOGGER: Final = logging.getLogger(__name__)
|
|
|
152
152
|
|
|
153
153
|
|
|
154
154
|
@inspector
|
|
155
|
-
def create_custom_data_points(device: hmd.Device) -> None:
|
|
155
|
+
def create_custom_data_points(*, device: hmd.Device) -> None:
|
|
156
156
|
"""Decides which data point category should be used, and creates the required data points."""
|
|
157
157
|
|
|
158
158
|
if device.ignore_for_custom_data_point:
|
|
@@ -163,7 +163,7 @@ def create_custom_data_points(device: hmd.Device) -> None:
|
|
|
163
163
|
device.model,
|
|
164
164
|
)
|
|
165
165
|
return
|
|
166
|
-
if data_point_definition_exists(device.model):
|
|
166
|
+
if data_point_definition_exists(model=device.model):
|
|
167
167
|
_LOGGER.debug(
|
|
168
168
|
"CREATE_CUSTOM_DATA_POINTS: Handling custom data point integration: %s, %s, %s",
|
|
169
169
|
device.interface_id,
|
|
@@ -174,4 +174,4 @@ def create_custom_data_points(device: hmd.Device) -> None:
|
|
|
174
174
|
# Call the custom creation function.
|
|
175
175
|
for custom_config in get_custom_configs(model=device.model):
|
|
176
176
|
for channel in device.channels.values():
|
|
177
|
-
custom_config.make_ce_func(channel, custom_config)
|
|
177
|
+
custom_config.make_ce_func(channel=channel, custom_config=custom_config)
|
|
@@ -191,6 +191,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
191
191
|
|
|
192
192
|
def __init__(
|
|
193
193
|
self,
|
|
194
|
+
*,
|
|
194
195
|
channel: hmd.Channel,
|
|
195
196
|
unique_id: str,
|
|
196
197
|
device_profile: DeviceProfile,
|
|
@@ -236,7 +237,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
236
237
|
)
|
|
237
238
|
|
|
238
239
|
@abstractmethod
|
|
239
|
-
def _manu_temp_changed(self, data_point: GenericDataPoint) -> None:
|
|
240
|
+
def _manu_temp_changed(self, *, data_point: GenericDataPoint | None = None, **kwargs: Any) -> None:
|
|
240
241
|
"""Handle device state changes."""
|
|
241
242
|
|
|
242
243
|
@state_property
|
|
@@ -354,6 +355,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
354
355
|
@bind_collector()
|
|
355
356
|
async def set_temperature(
|
|
356
357
|
self,
|
|
358
|
+
*,
|
|
357
359
|
temperature: float,
|
|
358
360
|
collector: CallParameterCollector | None = None,
|
|
359
361
|
do_validate: bool = True,
|
|
@@ -370,19 +372,19 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
370
372
|
await self._dp_setpoint.send_value(value=temperature, collector=collector, do_validate=do_validate)
|
|
371
373
|
|
|
372
374
|
@bind_collector()
|
|
373
|
-
async def set_mode(self, mode: ClimateMode, collector: CallParameterCollector | None = None) -> None:
|
|
375
|
+
async def set_mode(self, *, mode: ClimateMode, collector: CallParameterCollector | None = None) -> None:
|
|
374
376
|
"""Set new target mode."""
|
|
375
377
|
|
|
376
378
|
@bind_collector()
|
|
377
|
-
async def set_profile(self, profile: ClimateProfile, collector: CallParameterCollector | None = None) -> None:
|
|
379
|
+
async def set_profile(self, *, profile: ClimateProfile, collector: CallParameterCollector | None = None) -> None:
|
|
378
380
|
"""Set new profile."""
|
|
379
381
|
|
|
380
382
|
@inspector
|
|
381
|
-
async def enable_away_mode_by_calendar(self, start: datetime, end: datetime, away_temperature: float) -> None:
|
|
383
|
+
async def enable_away_mode_by_calendar(self, *, start: datetime, end: datetime, away_temperature: float) -> None:
|
|
382
384
|
"""Enable the away mode by calendar on thermostat."""
|
|
383
385
|
|
|
384
386
|
@inspector
|
|
385
|
-
async def enable_away_mode_by_duration(self, hours: int, away_temperature: float) -> None:
|
|
387
|
+
async def enable_away_mode_by_duration(self, *, hours: int, away_temperature: float) -> None:
|
|
386
388
|
"""Enable the away mode by duration on thermostat."""
|
|
387
389
|
|
|
388
390
|
@inspector
|
|
@@ -402,7 +404,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
402
404
|
return super().is_state_change(**kwargs)
|
|
403
405
|
|
|
404
406
|
@inspector
|
|
405
|
-
async def copy_schedule(self, target_climate_data_point: BaseCustomDpClimate) -> None:
|
|
407
|
+
async def copy_schedule(self, *, target_climate_data_point: BaseCustomDpClimate) -> None:
|
|
406
408
|
"""Copy schedule to target device."""
|
|
407
409
|
|
|
408
410
|
if self.schedule_profile_nos != target_climate_data_point.schedule_profile_nos:
|
|
@@ -417,6 +419,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
417
419
|
@inspector
|
|
418
420
|
async def copy_schedule_profile(
|
|
419
421
|
self,
|
|
422
|
+
*,
|
|
420
423
|
source_profile: ScheduleProfile,
|
|
421
424
|
target_profile: ScheduleProfile,
|
|
422
425
|
target_climate_data_point: BaseCustomDpClimate | None = None,
|
|
@@ -445,7 +448,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
445
448
|
)
|
|
446
449
|
|
|
447
450
|
@inspector
|
|
448
|
-
async def get_schedule_profile(self, profile: ScheduleProfile) -> PROFILE_DICT:
|
|
451
|
+
async def get_schedule_profile(self, *, profile: ScheduleProfile) -> PROFILE_DICT:
|
|
449
452
|
"""Return a schedule by climate profile."""
|
|
450
453
|
if not self._supports_schedule:
|
|
451
454
|
raise ValidationException(f"Schedule is not supported by device {self._device.name}")
|
|
@@ -453,7 +456,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
453
456
|
return schedule_data.get(profile, {})
|
|
454
457
|
|
|
455
458
|
@inspector
|
|
456
|
-
async def get_schedule_profile_weekday(self, profile: ScheduleProfile, weekday: ScheduleWeekday) -> WEEKDAY_DICT:
|
|
459
|
+
async def get_schedule_profile_weekday(self, *, profile: ScheduleProfile, weekday: ScheduleWeekday) -> WEEKDAY_DICT:
|
|
457
460
|
"""Return a schedule by climate profile."""
|
|
458
461
|
if not self._supports_schedule:
|
|
459
462
|
raise ValidationException(f"Schedule is not supported by device {self._device.name}")
|
|
@@ -474,7 +477,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
474
477
|
return raw_schedule
|
|
475
478
|
|
|
476
479
|
async def _get_schedule_profile(
|
|
477
|
-
self, profile: ScheduleProfile | None = None, weekday: ScheduleWeekday | None = None
|
|
480
|
+
self, *, profile: ScheduleProfile | None = None, weekday: ScheduleWeekday | None = None
|
|
478
481
|
) -> _SCHEDULE_DICT:
|
|
479
482
|
"""Get the schedule."""
|
|
480
483
|
schedule_data: _SCHEDULE_DICT = {}
|
|
@@ -506,7 +509,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
506
509
|
|
|
507
510
|
@inspector
|
|
508
511
|
async def set_schedule_profile(
|
|
509
|
-
self, profile: ScheduleProfile, profile_data: PROFILE_DICT, do_validate: bool = True
|
|
512
|
+
self, *, profile: ScheduleProfile, profile_data: PROFILE_DICT, do_validate: bool = True
|
|
510
513
|
) -> None:
|
|
511
514
|
"""Set a profile to device."""
|
|
512
515
|
await self._set_schedule_profile(
|
|
@@ -518,6 +521,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
518
521
|
|
|
519
522
|
async def _set_schedule_profile(
|
|
520
523
|
self,
|
|
524
|
+
*,
|
|
521
525
|
target_channel_address: str,
|
|
522
526
|
profile: ScheduleProfile,
|
|
523
527
|
profile_data: PROFILE_DICT,
|
|
@@ -547,6 +551,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
547
551
|
@inspector
|
|
548
552
|
async def set_simple_schedule_profile(
|
|
549
553
|
self,
|
|
554
|
+
*,
|
|
550
555
|
profile: ScheduleProfile,
|
|
551
556
|
base_temperature: float,
|
|
552
557
|
simple_profile_data: SIMPLE_PROFILE_DICT,
|
|
@@ -560,6 +565,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
560
565
|
@inspector
|
|
561
566
|
async def set_schedule_profile_weekday(
|
|
562
567
|
self,
|
|
568
|
+
*,
|
|
563
569
|
profile: ScheduleProfile,
|
|
564
570
|
weekday: ScheduleWeekday,
|
|
565
571
|
weekday_data: WEEKDAY_DICT,
|
|
@@ -588,6 +594,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
588
594
|
@inspector
|
|
589
595
|
async def set_simple_schedule_profile_weekday(
|
|
590
596
|
self,
|
|
597
|
+
*,
|
|
591
598
|
profile: ScheduleProfile,
|
|
592
599
|
weekday: ScheduleWeekday,
|
|
593
600
|
base_temperature: float,
|
|
@@ -600,7 +607,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
600
607
|
await self.set_schedule_profile_weekday(profile=profile, weekday=weekday, weekday_data=weekday_data)
|
|
601
608
|
|
|
602
609
|
def _validate_and_convert_simple_to_profile(
|
|
603
|
-
self, base_temperature: float, simple_profile_data: SIMPLE_PROFILE_DICT
|
|
610
|
+
self, *, base_temperature: float, simple_profile_data: SIMPLE_PROFILE_DICT
|
|
604
611
|
) -> PROFILE_DICT:
|
|
605
612
|
"""Convert simple profile dict to profile dict."""
|
|
606
613
|
profile_dict: PROFILE_DICT = {}
|
|
@@ -611,7 +618,7 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
611
618
|
return profile_dict
|
|
612
619
|
|
|
613
620
|
def _validate_and_convert_simple_to_profile_weekday(
|
|
614
|
-
self, base_temperature: float, simple_weekday_list: SIMPLE_WEEKDAY_LIST
|
|
621
|
+
self, *, base_temperature: float, simple_weekday_list: SIMPLE_WEEKDAY_LIST
|
|
615
622
|
) -> WEEKDAY_DICT:
|
|
616
623
|
"""Convert simple weekday list to weekday dict."""
|
|
617
624
|
if not self.min_temp <= base_temperature <= self.max_temp:
|
|
@@ -632,12 +639,16 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
632
639
|
if (temperature := slot.get(ScheduleSlotType.TEMPERATURE)) is None:
|
|
633
640
|
raise ValidationException("VALIDATE_PROFILE: TEMPERATURE is missing.")
|
|
634
641
|
|
|
635
|
-
if _convert_time_str_to_minutes(str(starttime)) >= _convert_time_str_to_minutes(
|
|
642
|
+
if _convert_time_str_to_minutes(time_str=str(starttime)) >= _convert_time_str_to_minutes(
|
|
643
|
+
time_str=str(endtime)
|
|
644
|
+
):
|
|
636
645
|
raise ValidationException(
|
|
637
646
|
f"VALIDATE_PROFILE: Start time {starttime} must lower than end time {endtime}"
|
|
638
647
|
)
|
|
639
648
|
|
|
640
|
-
if _convert_time_str_to_minutes(str(starttime)) < _convert_time_str_to_minutes(
|
|
649
|
+
if _convert_time_str_to_minutes(time_str=str(starttime)) < _convert_time_str_to_minutes(
|
|
650
|
+
time_str=previous_endtime
|
|
651
|
+
):
|
|
641
652
|
raise ValidationException(
|
|
642
653
|
f"VALIDATE_PROFILE: Timespans are overlapping with a previous slot for start time: {starttime} / end time: {endtime}"
|
|
643
654
|
)
|
|
@@ -648,7 +659,9 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
648
659
|
f"max: {self.max_temp}) for start time: {starttime} / end time: {endtime}"
|
|
649
660
|
)
|
|
650
661
|
|
|
651
|
-
if _convert_time_str_to_minutes(str(starttime)) > _convert_time_str_to_minutes(
|
|
662
|
+
if _convert_time_str_to_minutes(time_str=str(starttime)) > _convert_time_str_to_minutes(
|
|
663
|
+
time_str=previous_endtime
|
|
664
|
+
):
|
|
652
665
|
weekday_data[slot_no] = {
|
|
653
666
|
ScheduleSlotType.ENDTIME: starttime,
|
|
654
667
|
ScheduleSlotType.TEMPERATURE: base_temperature,
|
|
@@ -664,13 +677,14 @@ class BaseCustomDpClimate(CustomDataPoint):
|
|
|
664
677
|
|
|
665
678
|
return _fillup_weekday_data(base_temperature=base_temperature, weekday_data=weekday_data)
|
|
666
679
|
|
|
667
|
-
def _validate_schedule_profile(self, profile: ScheduleProfile, profile_data: PROFILE_DICT) -> None:
|
|
680
|
+
def _validate_schedule_profile(self, *, profile: ScheduleProfile, profile_data: PROFILE_DICT) -> None:
|
|
668
681
|
"""Validate the profile."""
|
|
669
682
|
for weekday, weekday_data in profile_data.items():
|
|
670
683
|
self._validate_schedule_profile_weekday(profile=profile, weekday=weekday, weekday_data=weekday_data)
|
|
671
684
|
|
|
672
685
|
def _validate_schedule_profile_weekday(
|
|
673
686
|
self,
|
|
687
|
+
*,
|
|
674
688
|
profile: ScheduleProfile,
|
|
675
689
|
weekday: ScheduleWeekday,
|
|
676
690
|
weekday_data: WEEKDAY_DICT,
|
|
@@ -720,7 +734,7 @@ class CustomDpSimpleRfThermostat(BaseCustomDpClimate):
|
|
|
720
734
|
|
|
721
735
|
__slots__ = ()
|
|
722
736
|
|
|
723
|
-
def _manu_temp_changed(self, data_point: GenericDataPoint) -> None:
|
|
737
|
+
def _manu_temp_changed(self, *, data_point: GenericDataPoint | None = None, **kwargs: Any) -> None:
|
|
724
738
|
"""Handle device state changes."""
|
|
725
739
|
|
|
726
740
|
|
|
@@ -741,6 +755,7 @@ class CustomDpRfThermostat(BaseCustomDpClimate):
|
|
|
741
755
|
|
|
742
756
|
def __init__(
|
|
743
757
|
self,
|
|
758
|
+
*,
|
|
744
759
|
channel: hmd.Channel,
|
|
745
760
|
unique_id: str,
|
|
746
761
|
device_profile: DeviceProfile,
|
|
@@ -788,7 +803,7 @@ class CustomDpRfThermostat(BaseCustomDpClimate):
|
|
|
788
803
|
)
|
|
789
804
|
)
|
|
790
805
|
|
|
791
|
-
def _manu_temp_changed(self, data_point: GenericDataPoint) -> None:
|
|
806
|
+
def _manu_temp_changed(self, *, data_point: GenericDataPoint | None = None, **kwargs: Any) -> None:
|
|
792
807
|
"""Handle device state changes."""
|
|
793
808
|
if (
|
|
794
809
|
data_point == self._dp_control_mode
|
|
@@ -861,7 +876,7 @@ class CustomDpRfThermostat(BaseCustomDpClimate):
|
|
|
861
876
|
return self._dp_temperature_offset.value
|
|
862
877
|
|
|
863
878
|
@bind_collector()
|
|
864
|
-
async def set_mode(self, mode: ClimateMode, collector: CallParameterCollector | None = None) -> None:
|
|
879
|
+
async def set_mode(self, *, mode: ClimateMode, collector: CallParameterCollector | None = None) -> None:
|
|
865
880
|
"""Set new mode."""
|
|
866
881
|
if not self.is_state_change(mode=mode):
|
|
867
882
|
return
|
|
@@ -876,7 +891,7 @@ class CustomDpRfThermostat(BaseCustomDpClimate):
|
|
|
876
891
|
await self.set_temperature(temperature=_OFF_TEMPERATURE, collector=collector, do_validate=False)
|
|
877
892
|
|
|
878
893
|
@bind_collector()
|
|
879
|
-
async def set_profile(self, profile: ClimateProfile, collector: CallParameterCollector | None = None) -> None:
|
|
894
|
+
async def set_profile(self, *, profile: ClimateProfile, collector: CallParameterCollector | None = None) -> None:
|
|
880
895
|
"""Set new profile."""
|
|
881
896
|
if not self.is_state_change(profile=profile):
|
|
882
897
|
return
|
|
@@ -896,7 +911,7 @@ class CustomDpRfThermostat(BaseCustomDpClimate):
|
|
|
896
911
|
)
|
|
897
912
|
|
|
898
913
|
@inspector
|
|
899
|
-
async def enable_away_mode_by_calendar(self, start: datetime, end: datetime, away_temperature: float) -> None:
|
|
914
|
+
async def enable_away_mode_by_calendar(self, *, start: datetime, end: datetime, away_temperature: float) -> None:
|
|
900
915
|
"""Enable the away mode by calendar on thermostat."""
|
|
901
916
|
await self._client.set_value(
|
|
902
917
|
channel_address=self._channel.address,
|
|
@@ -906,7 +921,7 @@ class CustomDpRfThermostat(BaseCustomDpClimate):
|
|
|
906
921
|
)
|
|
907
922
|
|
|
908
923
|
@inspector
|
|
909
|
-
async def enable_away_mode_by_duration(self, hours: int, away_temperature: float) -> None:
|
|
924
|
+
async def enable_away_mode_by_duration(self, *, hours: int, away_temperature: float) -> None:
|
|
910
925
|
"""Enable the away mode by duration on thermostat."""
|
|
911
926
|
start = datetime.now() - timedelta(minutes=10)
|
|
912
927
|
end = datetime.now() + timedelta(hours=hours)
|
|
@@ -954,7 +969,7 @@ class CustomDpRfThermostat(BaseCustomDpClimate):
|
|
|
954
969
|
return profiles
|
|
955
970
|
|
|
956
971
|
|
|
957
|
-
def _party_mode_code(start: datetime, end: datetime, away_temperature: float) -> str:
|
|
972
|
+
def _party_mode_code(*, start: datetime, end: datetime, away_temperature: float) -> str:
|
|
958
973
|
"""
|
|
959
974
|
Create the party mode code.
|
|
960
975
|
|
|
@@ -983,6 +998,7 @@ class CustomDpIpThermostat(BaseCustomDpClimate):
|
|
|
983
998
|
|
|
984
999
|
def __init__(
|
|
985
1000
|
self,
|
|
1001
|
+
*,
|
|
986
1002
|
channel: hmd.Channel,
|
|
987
1003
|
unique_id: str,
|
|
988
1004
|
device_profile: DeviceProfile,
|
|
@@ -1032,7 +1048,7 @@ class CustomDpIpThermostat(BaseCustomDpClimate):
|
|
|
1032
1048
|
)
|
|
1033
1049
|
)
|
|
1034
1050
|
|
|
1035
|
-
def _manu_temp_changed(self, data_point: GenericDataPoint) -> None:
|
|
1051
|
+
def _manu_temp_changed(self, *, data_point: GenericDataPoint | None = None, **kwargs: Any) -> None:
|
|
1036
1052
|
"""Handle device state changes."""
|
|
1037
1053
|
if (
|
|
1038
1054
|
data_point == self._dp_set_point_mode
|
|
@@ -1135,7 +1151,7 @@ class CustomDpIpThermostat(BaseCustomDpClimate):
|
|
|
1135
1151
|
return self._dp_temperature_offset.value
|
|
1136
1152
|
|
|
1137
1153
|
@bind_collector()
|
|
1138
|
-
async def set_mode(self, mode: ClimateMode, collector: CallParameterCollector | None = None) -> None:
|
|
1154
|
+
async def set_mode(self, *, mode: ClimateMode, collector: CallParameterCollector | None = None) -> None:
|
|
1139
1155
|
"""Set new target mode."""
|
|
1140
1156
|
if not self.is_state_change(mode=mode):
|
|
1141
1157
|
return
|
|
@@ -1153,7 +1169,7 @@ class CustomDpIpThermostat(BaseCustomDpClimate):
|
|
|
1153
1169
|
await self.set_temperature(temperature=_OFF_TEMPERATURE, collector=collector, do_validate=False)
|
|
1154
1170
|
|
|
1155
1171
|
@bind_collector()
|
|
1156
|
-
async def set_profile(self, profile: ClimateProfile, collector: CallParameterCollector | None = None) -> None:
|
|
1172
|
+
async def set_profile(self, *, profile: ClimateProfile, collector: CallParameterCollector | None = None) -> None:
|
|
1157
1173
|
"""Set new control mode."""
|
|
1158
1174
|
if not self.is_state_change(profile=profile):
|
|
1159
1175
|
return
|
|
@@ -1169,7 +1185,7 @@ class CustomDpIpThermostat(BaseCustomDpClimate):
|
|
|
1169
1185
|
await self._dp_active_profile.send_value(value=profile_idx, collector=collector)
|
|
1170
1186
|
|
|
1171
1187
|
@inspector
|
|
1172
|
-
async def enable_away_mode_by_calendar(self, start: datetime, end: datetime, away_temperature: float) -> None:
|
|
1188
|
+
async def enable_away_mode_by_calendar(self, *, start: datetime, end: datetime, away_temperature: float) -> None:
|
|
1173
1189
|
"""Enable the away mode by calendar on thermostat."""
|
|
1174
1190
|
await self._client.put_paramset(
|
|
1175
1191
|
channel_address=self._channel.address,
|
|
@@ -1183,7 +1199,7 @@ class CustomDpIpThermostat(BaseCustomDpClimate):
|
|
|
1183
1199
|
)
|
|
1184
1200
|
|
|
1185
1201
|
@inspector
|
|
1186
|
-
async def enable_away_mode_by_duration(self, hours: int, away_temperature: float) -> None:
|
|
1202
|
+
async def enable_away_mode_by_duration(self, *, hours: int, away_temperature: float) -> None:
|
|
1187
1203
|
"""Enable the away mode by duration on thermostat."""
|
|
1188
1204
|
start = datetime.now() - timedelta(minutes=10)
|
|
1189
1205
|
end = datetime.now() + timedelta(hours=hours)
|
|
@@ -1243,7 +1259,7 @@ def _convert_minutes_to_time_str(minutes: Any) -> str:
|
|
|
1243
1259
|
return time_str
|
|
1244
1260
|
|
|
1245
1261
|
|
|
1246
|
-
def _convert_time_str_to_minutes(time_str: str) -> int:
|
|
1262
|
+
def _convert_time_str_to_minutes(*, time_str: str) -> int:
|
|
1247
1263
|
"""Convert minutes to a time string."""
|
|
1248
1264
|
if SCHEDULER_TIME_PATTERN.match(time_str) is None:
|
|
1249
1265
|
raise ValidationException(
|
|
@@ -1256,17 +1272,18 @@ def _convert_time_str_to_minutes(time_str: str) -> int:
|
|
|
1256
1272
|
raise ValidationException(f"Failed to convert time {time_str}. Format must be hh:mm.") from exc
|
|
1257
1273
|
|
|
1258
1274
|
|
|
1259
|
-
def _sort_simple_weekday_list(simple_weekday_list: SIMPLE_WEEKDAY_LIST) -> SIMPLE_WEEKDAY_LIST:
|
|
1275
|
+
def _sort_simple_weekday_list(*, simple_weekday_list: SIMPLE_WEEKDAY_LIST) -> SIMPLE_WEEKDAY_LIST:
|
|
1260
1276
|
"""Sort simple weekday list."""
|
|
1261
1277
|
simple_weekday_dict = sorted(
|
|
1262
1278
|
{
|
|
1263
|
-
_convert_time_str_to_minutes(str(slot[ScheduleSlotType.STARTTIME])): slot
|
|
1279
|
+
_convert_time_str_to_minutes(time_str=str(slot[ScheduleSlotType.STARTTIME])): slot
|
|
1280
|
+
for slot in simple_weekday_list
|
|
1264
1281
|
}.items()
|
|
1265
1282
|
)
|
|
1266
1283
|
return [slot[1] for slot in simple_weekday_dict]
|
|
1267
1284
|
|
|
1268
1285
|
|
|
1269
|
-
def _fillup_weekday_data(base_temperature: float, weekday_data: WEEKDAY_DICT) -> WEEKDAY_DICT:
|
|
1286
|
+
def _fillup_weekday_data(*, base_temperature: float, weekday_data: WEEKDAY_DICT) -> WEEKDAY_DICT:
|
|
1270
1287
|
"""Fillup weekday data."""
|
|
1271
1288
|
for slot_no in SCHEDULE_SLOT_IN_RANGE:
|
|
1272
1289
|
if slot_no not in weekday_data:
|
|
@@ -1278,7 +1295,7 @@ def _fillup_weekday_data(base_temperature: float, weekday_data: WEEKDAY_DICT) ->
|
|
|
1278
1295
|
return weekday_data
|
|
1279
1296
|
|
|
1280
1297
|
|
|
1281
|
-
def _get_raw_schedule_paramset(schedule_data: _SCHEDULE_DICT) -> _RAW_SCHEDULE_DICT:
|
|
1298
|
+
def _get_raw_schedule_paramset(*, schedule_data: _SCHEDULE_DICT) -> _RAW_SCHEDULE_DICT:
|
|
1282
1299
|
"""Return the raw paramset."""
|
|
1283
1300
|
raw_paramset: _RAW_SCHEDULE_DICT = {}
|
|
1284
1301
|
for profile, profile_data in schedule_data.items():
|
|
@@ -1290,12 +1307,13 @@ def _get_raw_schedule_paramset(schedule_data: _SCHEDULE_DICT) -> _RAW_SCHEDULE_D
|
|
|
1290
1307
|
raise ValidationException(f"Not a valid profile name: {raw_profile_name}")
|
|
1291
1308
|
raw_value: float | int = cast(float | int, slot_value)
|
|
1292
1309
|
if slot_type == ScheduleSlotType.ENDTIME and isinstance(slot_value, str):
|
|
1293
|
-
raw_value = _convert_time_str_to_minutes(slot_value)
|
|
1310
|
+
raw_value = _convert_time_str_to_minutes(time_str=slot_value)
|
|
1294
1311
|
raw_paramset[raw_profile_name] = raw_value
|
|
1295
1312
|
return raw_paramset
|
|
1296
1313
|
|
|
1297
1314
|
|
|
1298
1315
|
def _add_to_schedule_data(
|
|
1316
|
+
*,
|
|
1299
1317
|
schedule_data: _SCHEDULE_DICT,
|
|
1300
1318
|
profile: ScheduleProfile,
|
|
1301
1319
|
weekday: ScheduleWeekday,
|
|
@@ -1317,6 +1335,7 @@ def _add_to_schedule_data(
|
|
|
1317
1335
|
|
|
1318
1336
|
|
|
1319
1337
|
def make_simple_thermostat(
|
|
1338
|
+
*,
|
|
1320
1339
|
channel: hmd.Channel,
|
|
1321
1340
|
custom_config: CustomConfig,
|
|
1322
1341
|
) -> None:
|
|
@@ -1330,6 +1349,7 @@ def make_simple_thermostat(
|
|
|
1330
1349
|
|
|
1331
1350
|
|
|
1332
1351
|
def make_thermostat(
|
|
1352
|
+
*,
|
|
1333
1353
|
channel: hmd.Channel,
|
|
1334
1354
|
custom_config: CustomConfig,
|
|
1335
1355
|
) -> None:
|
|
@@ -1343,6 +1363,7 @@ def make_thermostat(
|
|
|
1343
1363
|
|
|
1344
1364
|
|
|
1345
1365
|
def make_thermostat_group(
|
|
1366
|
+
*,
|
|
1346
1367
|
channel: hmd.Channel,
|
|
1347
1368
|
custom_config: CustomConfig,
|
|
1348
1369
|
) -> None:
|
|
@@ -1356,6 +1377,7 @@ def make_thermostat_group(
|
|
|
1356
1377
|
|
|
1357
1378
|
|
|
1358
1379
|
def make_ip_thermostat(
|
|
1380
|
+
*,
|
|
1359
1381
|
channel: hmd.Channel,
|
|
1360
1382
|
custom_config: CustomConfig,
|
|
1361
1383
|
) -> None:
|
|
@@ -1369,6 +1391,7 @@ def make_ip_thermostat(
|
|
|
1369
1391
|
|
|
1370
1392
|
|
|
1371
1393
|
def make_ip_thermostat_group(
|
|
1394
|
+
*,
|
|
1372
1395
|
channel: hmd.Channel,
|
|
1373
1396
|
custom_config: CustomConfig,
|
|
1374
1397
|
) -> None:
|