python-hilo 2026.11.1__tar.gz → 2026.11.2__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.
Files changed (73) hide show
  1. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/PKG-INFO +1 -1
  2. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/const.py +1 -1
  3. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/device/climate.py +13 -6
  4. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyproject.toml +1 -1
  5. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/tests/test_climate.py +27 -0
  6. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/uv.lock +7 -17
  7. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.bandit.yaml +0 -0
  8. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.devcontainer/devcontainer-lock.json +0 -0
  9. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.devcontainer/devcontainer.json +0 -0
  10. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.editorconfig +0 -0
  11. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.gitattributes +0 -0
  12. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  13. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.github/ISSUE_TEMPLATE/issue.md +0 -0
  14. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.github/dependabot.yml +0 -0
  15. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.github/labels.yml +0 -0
  16. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.github/release-drafter.yml +0 -0
  17. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.github/workflows/labeler.yml +0 -0
  18. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.github/workflows/release-drafter.yml +0 -0
  19. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.github/workflows/tests.yaml +0 -0
  20. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.gitignore +0 -0
  21. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/.pre-commit-config.yaml +0 -0
  22. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/AGENTS.md +0 -0
  23. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/LICENSE +0 -0
  24. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/README.md +0 -0
  25. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/README.md +0 -0
  26. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/graphql/onAnyDeviceUpdated-cee.json +0 -0
  27. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/graphql/onAnyDeviceUpdated-meter.json +0 -0
  28. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/graphql/onAnyDeviceUpdated-tstat.json +0 -0
  29. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/postman/hilo-prod.postman_environment.json +0 -0
  30. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/postman/hilo.postman_collection.json +0 -0
  31. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/rest/location_weather.json +0 -0
  32. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/rest/season_events_ch_2025.json +0 -0
  33. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/rest/season_events_ch_2026_empty.json +0 -0
  34. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/rest/season_events_hilo_2024.json +0 -0
  35. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/rest/seasons_summary.json +0 -0
  36. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/ChallengeAdded.json +0 -0
  37. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/EventAdded.json +0 -0
  38. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/EventCHConsumptionUpdatedValuesReceived-during-event.json +0 -0
  39. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/EventCHConsumptionUpdatedValuesReceived.json +0 -0
  40. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/EventCHDetailsInitialValuesReceived.json +0 -0
  41. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/EventCHDetailsUpdatedValuesReceived.json +0 -0
  42. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/EventFlexConsumptionUpdatedValuesReceived.json +0 -0
  43. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/EventFlexDetailsInitialValuesReceived.json +0 -0
  44. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/EventFlexDetailsUpdatedValuesReceived.json +0 -0
  45. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/legacy-pre-2025/ChallengeAdded.json +0 -0
  46. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/legacy-pre-2025/ChallengeConsumptionUpdatedValuesReceived.json +0 -0
  47. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/legacy-pre-2025/ChallengeDetailsInitialValuesReceived.json +0 -0
  48. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/legacy-pre-2025/ChallengeListInitialValuesReceived.json +0 -0
  49. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/challengehub/legacy-pre-2025/ChallengeListUpdatedValuesReceived.json +0 -0
  50. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/doc/signalr/devicehub/DeviceListInitialValuesReceived.json +0 -0
  51. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/__init__.py +0 -0
  52. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/api.py +0 -0
  53. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/auth.py +0 -0
  54. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/device/__init__.py +0 -0
  55. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/device/graphql_value_mapper.py +0 -0
  56. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/device/light.py +0 -0
  57. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/device/sensor.py +0 -0
  58. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/device/switch.py +0 -0
  59. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/devices.py +0 -0
  60. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/event.py +0 -0
  61. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/exceptions.py +0 -0
  62. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/graphql.py +0 -0
  63. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/oauth2helper.py +0 -0
  64. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/signalr.py +0 -0
  65. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/util/__init__.py +0 -0
  66. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/pyhilo/util/state.py +0 -0
  67. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/tests/__init__.py +0 -0
  68. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/tests/test_api_auth.py +0 -0
  69. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/tests/test_device.py +0 -0
  70. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/tests/test_graphql_value_mapper.py +0 -0
  71. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/tests/test_signalr.py +0 -0
  72. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/tests/test_util.py +0 -0
  73. {python_hilo-2026.11.1 → python_hilo-2026.11.2}/tests/test_util_state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-hilo
3
- Version: 2026.11.1
3
+ Version: 2026.11.2
4
4
  Summary: A Python3, async interface to the Hilo API
5
5
  Project-URL: Repository, https://github.com/dvd-dev/python-hilo
6
6
  Author-email: David Vallee Delisle <me@dvd.dev>
@@ -11,7 +11,7 @@ INSTANCE_ID: Final = str(uuid.uuid4())[24:]
11
11
  LOG: Final = logging.getLogger(__package__)
12
12
  DEFAULT_STATE_FILE: Final = "hilo_state.yaml"
13
13
  REQUEST_RETRY: Final = 9
14
- PYHILO_VERSION: Final = "2026.11.01"
14
+ PYHILO_VERSION: Final = "2026.11.02"
15
15
  # TODO: Find a way to keep previous line in sync with pyproject.toml automatically
16
16
 
17
17
  CONTENT_TYPE_FORM: Final = "application/x-www-form-urlencoded"
@@ -102,9 +102,20 @@ class Climate(HiloDevice):
102
102
  """
103
103
  Gets the current HVAC action of the device.
104
104
 
105
+ For 24 V thermostats the action comes from currentState ("HEATING",
106
+ "COOLING", "OFF"). For baseboard thermostats it is derived from the
107
+ reported heating power.
108
+
105
109
  Returns:
106
- str: 'heating' if heating is active, 'idle' otherwise.
110
+ str: one of 'heating', 'cooling', or 'idle'.
107
111
  """
112
+ if self.is_low_voltage:
113
+ state = self.current_state
114
+ if state == "HEATING":
115
+ return "heating"
116
+ if state == "COOLING":
117
+ return "cooling"
118
+ return "idle"
108
119
  attr = self.get_value("heating", 0)
109
120
  return "heating" if attr > 0 else "idle"
110
121
 
@@ -170,11 +181,7 @@ class Climate(HiloDevice):
170
181
 
171
182
  @property
172
183
  def current_state(self) -> str | None:
173
- """Raw running state reported by a 24 V thermostat.
174
-
175
- Exposed for consumers, but deliberately not mapped to hvac_action yet:
176
- its vocabulary has not been observed in enough operating conditions.
177
- """
184
+ """Raw running state reported by a 24 V thermostat ("HEATING", "COOLING", "OFF")."""
178
185
  return self._optional_str("current_state")
179
186
 
180
187
  @property
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-hilo"
3
- version = "2026.11.1"
3
+ version = "2026.11.2"
4
4
  description = "A Python3, async interface to the Hilo API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -41,6 +41,33 @@ def _climate(**readings):
41
41
  return device
42
42
 
43
43
 
44
+ class TestHvacAction:
45
+ @pytest.mark.parametrize(
46
+ "current_state, expected",
47
+ [
48
+ pytest.param("HEATING", "heating", id="heating"),
49
+ pytest.param("COOLING", "cooling", id="cooling"),
50
+ pytest.param("OFF", "idle", id="off-is-idle"),
51
+ pytest.param(None, "idle", id="unreported-is-idle"),
52
+ ],
53
+ )
54
+ def test_low_voltage_maps_current_state(self, current_state, expected):
55
+ kwargs = {"Thermostat24VMode": "AUTO"}
56
+ if current_state is not None:
57
+ kwargs["CurrentState"] = current_state
58
+ assert _climate(**kwargs).hvac_action == expected
59
+
60
+ @pytest.mark.parametrize(
61
+ "heating, expected",
62
+ [
63
+ pytest.param(1, "heating", id="power-on-is-heating"),
64
+ pytest.param(0, "idle", id="power-off-is-idle"),
65
+ ],
66
+ )
67
+ def test_baseboard_uses_heating_power(self, heating, expected):
68
+ assert _climate(Heating=heating).hvac_action == expected
69
+
70
+
44
71
  class TestAsList:
45
72
  def test_none_is_empty(self):
46
73
  assert as_list(None) == []
@@ -907,14 +907,14 @@ wheels = [
907
907
 
908
908
  [[package]]
909
909
  name = "pytest-asyncio"
910
- version = "1.4.1"
910
+ version = "1.4.1.post1"
911
911
  source = { registry = "https://pypi.org/simple" }
912
912
  dependencies = [
913
913
  { name = "pytest" },
914
914
  ]
915
- sdist = { url = "https://files.pythonhosted.org/packages/f8/9a/887535e477847f5159377ab35303923644840b13c0ca03c0a467f84d4680/pytest_asyncio-1.4.1.tar.gz", hash = "sha256:e8266eb094e5ee9ffdb2068c9836f33e209ea77a970eb846933d416b5602669b", size = 145237, upload-time = "2026-10-25T21:05:37.135Z" }
915
+ sdist = { url = "https://files.pythonhosted.org/packages/73/7f/c60a4338420b33453d4f814ac0c930abd399f162b9bdd21af4b9e363037f/pytest_asyncio-1.4.1.post1.tar.gz", hash = "sha256:f582d9ea4a1c082195abe8bd29a1fe106aeb1cd435e3dad2d3bc2749935c36a2", size = 145360, upload-time = "2026-11-01T18:26:03.732Z" }
916
916
  wheels = [
917
- { url = "https://files.pythonhosted.org/packages/46/d7/cd9e2708afdbf059658adaf08bdb0792ca52489871051d6ce7d498b51277/pytest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:507afa78ec77c6661164ef1a2368bc7820383fd1a14b2a7f813de8b5a92949cc", size = 17061, upload-time = "2026-10-25T21:05:35.473Z" },
917
+ { url = "https://files.pythonhosted.org/packages/e3/de/adbc7578959cfab07e5e7c05cf6d54224a51e7667ea2c07458b6bc571e75/pytest_asyncio-1.4.1.post1-py3-none-any.whl", hash = "sha256:0755e488cc0a2b9bd2eab7292cc6390d4cbb8390f1020745647bae5a708e0165", size = 17140, upload-time = "2026-11-01T18:26:02.168Z" },
918
918
  ]
919
919
 
920
920
  [[package]]
@@ -933,15 +933,14 @@ wheels = [
933
933
 
934
934
  [[package]]
935
935
  name = "python-dateutil"
936
- version = "3.0.0"
936
+ version = "4.0.0"
937
937
  source = { registry = "https://pypi.org/simple" }
938
938
  dependencies = [
939
- { name = "six" },
940
939
  { name = "tzdata" },
941
940
  ]
942
- sdist = { url = "https://files.pythonhosted.org/packages/0e/df/f15f80383f152452d8985b36d95cb8751f901a3e0ff4cd27f7e118f168e2/python_dateutil-3.0.0.tar.gz", hash = "sha256:7ebcb3c29a92f3ceafe79a7a48a5c45677e9e004bbeec5742ac0bbb00560b7a2", size = 271066, upload-time = "2026-10-21T23:41:45.075Z" }
941
+ sdist = { url = "https://files.pythonhosted.org/packages/18/39/b3927f3616f6223832f9b760e4208e8a7c7e1dea2b4d9ad7d32a5d214c3a/python_dateutil-4.0.0.tar.gz", hash = "sha256:d7295ff14fdc0f92f662100bfb6c551d160ca090f2939f1ec5b9794265c03f06", size = 250419, upload-time = "2026-11-01T07:02:42.426Z" }
943
942
  wheels = [
944
- { url = "https://files.pythonhosted.org/packages/5a/17/cebee01d1c0c1441174cf0456ddc91b280d79acb65340c05e41d77866ee8/python_dateutil-3.0.0-py2.py3-none-any.whl", hash = "sha256:3c79630202e4152c6add678446984236efe4f57e84d8e892aab0637bab82e9fd", size = 94659, upload-time = "2026-10-21T23:41:43.431Z" },
943
+ { url = "https://files.pythonhosted.org/packages/2f/a9/52b89563d154433d79b51ed1eb4842bf7334cb2696891a7fe135618853d7/python_dateutil-4.0.0-py3-none-any.whl", hash = "sha256:a5e246f79eb80475e691bf93862cead98decd8a498cf53ba0516fcdc0f50dc1c", size = 92853, upload-time = "2026-11-01T07:02:40.797Z" },
945
944
  ]
946
945
 
947
946
  [[package]]
@@ -958,7 +957,7 @@ wheels = [
958
957
 
959
958
  [[package]]
960
959
  name = "python-hilo"
961
- version = "2026.11.1"
960
+ version = "2026.11.2"
962
961
  source = { editable = "." }
963
962
  dependencies = [
964
963
  { name = "aiofiles" },
@@ -1064,15 +1063,6 @@ wheels = [
1064
1063
  { url = "https://files.pythonhosted.org/packages/99/d5/4ad4ed31413c301430918e70d3d38ae6f097af0766cb9ab8c9d502b2a4d6/ruff-0.17.3-py3-none-win_arm64.whl", hash = "sha256:81fce74bcd6be0e9da7c67c7553fd0f1f12964a2052cd728ef917b937a5ca1d9", size = 9854332, upload-time = "2026-10-27T00:34:03.847Z" },
1065
1064
  ]
1066
1065
 
1067
- [[package]]
1068
- name = "six"
1069
- version = "1.17.0"
1070
- source = { registry = "https://pypi.org/simple" }
1071
- sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
1072
- wheels = [
1073
- { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
1074
- ]
1075
-
1076
1066
  [[package]]
1077
1067
  name = "typing-extensions"
1078
1068
  version = "4.16.0"
File without changes