dimplex-controller 0.15.1__tar.gz → 0.16.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dimplex-controller
3
- Version: 0.15.1
3
+ Version: 0.16.0
4
4
  Summary: Unofficial async Python client for Dimplex (GDHV IoT) heating appliances — not affiliated with Glen Dimplex
5
5
  Keywords: dimplex,heating,control,iot,asyncio
6
6
  Author: Kieran Roper
@@ -42,6 +42,7 @@ from .models import (
42
42
  Hub,
43
43
  HygieneFrequency,
44
44
  ProductModel,
45
+ ScheduleProfile,
45
46
  SetbackStatus,
46
47
  TimerMode,
47
48
  TimerModeSettings,
@@ -416,19 +417,38 @@ class DimplexControl:
416
417
  return []
417
418
  return [ProductModel.model_validate(item) for item in data]
418
419
 
419
- async def get_appliance_features(self, hub_id: str, appliance_id: str) -> TimerModeSettings:
420
- """Get timer details (and mode) for an appliance."""
420
+ async def get_appliance_features(
421
+ self,
422
+ hub_id: str,
423
+ appliance_id: str,
424
+ *,
425
+ timer_mode: int | ScheduleProfile = 0,
426
+ ) -> TimerModeSettings:
427
+ """Get a profile's timer periods for an appliance.
428
+
429
+ ``timer_mode`` selects which profile's periods come back (0 = user
430
+ timer, 1 = home all day, 2 = out all day); the app fetches each profile
431
+ separately and caches one entry per value. The response ``TimerMode``
432
+ echoes the request — it is not the active schedule, which lives in
433
+ ``ApplianceStatus.StatusTwo`` (dimplex-controller-py#149).
434
+ """
421
435
  payload = {
422
436
  "HubId": hub_id,
423
437
  "ApplianceId": appliance_id,
424
- "TimerMode": 0, # Required field in request; value ignored on read
438
+ "TimerMode": int(timer_mode),
425
439
  }
426
440
  data = await self._request("POST", "/RemoteControl/GetTimerModeDetailsForAppliance", json=payload)
427
441
  return TimerModeSettings.model_validate(data)
428
442
 
429
- async def get_schedule(self, hub_id: str, appliance_id: str) -> TimerModeSettings:
430
- """Return the current timer mode + periods (alias of :meth:`get_appliance_features`)."""
431
- return await self.get_appliance_features(hub_id, appliance_id)
443
+ async def get_schedule(
444
+ self,
445
+ hub_id: str,
446
+ appliance_id: str,
447
+ *,
448
+ timer_mode: int | ScheduleProfile = 0,
449
+ ) -> TimerModeSettings:
450
+ """Return a profile's timer periods (alias of :meth:`get_appliance_features`)."""
451
+ return await self.get_appliance_features(hub_id, appliance_id, timer_mode=timer_mode)
432
452
 
433
453
  async def _write_timer_settings(self, settings: TimerModeSettings) -> TimerModeSettings:
434
454
  """POST a full :class:`TimerModeSettings` payload and return it."""
@@ -572,6 +592,7 @@ class DimplexControl:
572
592
  until: datetime | str | None = None,
573
593
  number_of_days: int = 0,
574
594
  frequency: int | HygieneFrequency = 0,
595
+ status_two: int | ScheduleProfile = 0,
575
596
  endpoint: str = "/RemoteControl/SetApplianceMode",
576
597
  ) -> None:
577
598
  """Engage or clear a single :class:`ApplianceModeFlag`.
@@ -588,6 +609,7 @@ class DimplexControl:
588
609
  Date=_iso_away_until(until) if enable else NULL_DATETIME,
589
610
  NumberOfDays=int(number_of_days) if enable else 0,
590
611
  Frequency=int(frequency) if enable else 0,
612
+ StatusTwo=int(status_two) if enable else 0,
591
613
  )
592
614
  payload = {
593
615
  "Settings": settings.model_dump(mode="json"),
@@ -788,6 +810,26 @@ class DimplexControl:
788
810
  temperature=temperature,
789
811
  )
790
812
 
813
+ async def set_schedule_profile(
814
+ self,
815
+ hub_id: str,
816
+ appliance_ids: list[str],
817
+ profile: int | ScheduleProfile,
818
+ ) -> None:
819
+ """Apply a schedule profile (User timer / Home all day / Out all day).
820
+
821
+ Sends ``ApplianceModes=1`` (:attr:`TIMER_MODE`) with ``Status=1`` and
822
+ the profile in ``StatusTwo`` — the exact payload the official app's
823
+ schedule picker sends (dimplex-controller-py#147). Accepts a
824
+ :class:`ScheduleProfile` or its int value.
825
+ """
826
+ await self.set_mode_flag(
827
+ hub_id,
828
+ appliance_ids,
829
+ ApplianceModeFlag.TIMER_MODE,
830
+ status_two=int(profile),
831
+ )
832
+
791
833
  async def set_appliance_setpoint_temperature(
792
834
  self, hub_id: str, appliance_ids: list[str], temperature: float
793
835
  ) -> None:
@@ -15,6 +15,13 @@ class TimerMode(IntEnum):
15
15
 
16
16
  Exact product names vary by appliance firmware; these are the values used
17
17
  by the Dimplex Control mobile client and reverse-engineered traffic.
18
+
19
+ .. caution::
20
+ App 2.29.1 uses 0/1/2 here as the *profile* domain (user timer, home all
21
+ day, out all day) and has no manual/frost/off timer modes — those go
22
+ through ``SetApplianceMode`` mode bits instead. ``MANUAL``,
23
+ ``FROST_PROTECTION`` and ``OFF`` are therefore unverified in that client
24
+ and kept only because consumers map them (dimplex-controller-py#149).
18
25
  """
19
26
 
20
27
  USER_TIMER = 0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dimplex-controller"
3
- version = "0.15.1"
3
+ version = "0.16.0"
4
4
  description = "Unofficial async Python client for Dimplex (GDHV IoT) heating appliances — not affiliated with Glen Dimplex"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dimplex-controller"
3
- version = "0.15.1"
3
+ version = "0.16.0"
4
4
  description = "Unofficial async Python client for Dimplex (GDHV IoT) heating appliances — not affiliated with Glen Dimplex"
5
5
  authors = [{ name = "Kieran Roper" }]
6
6
  license = "MIT"