PyPlumIO 0.5.49__tar.gz → 0.5.50__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 (163) hide show
  1. {pyplumio-0.5.49 → pyplumio-0.5.50}/PKG-INFO +1 -1
  2. {pyplumio-0.5.49 → pyplumio-0.5.50}/PyPlumIO.egg-info/PKG-INFO +1 -1
  3. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/_version.py +2 -2
  4. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/const.py +2 -0
  5. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/devices/__init__.py +8 -3
  6. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/devices/ecomax.py +9 -9
  7. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/devices/mixer.py +2 -2
  8. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/devices/thermostat.py +2 -2
  9. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/protocol.py +10 -13
  10. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/devices/test_ecomax.py +19 -4
  11. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/devices/test_init.py +20 -5
  12. {pyplumio-0.5.49 → pyplumio-0.5.50}/.gitattributes +0 -0
  13. {pyplumio-0.5.49 → pyplumio-0.5.50}/.github/CODE_OF_CONDUCT.md +0 -0
  14. {pyplumio-0.5.49 → pyplumio-0.5.50}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  15. {pyplumio-0.5.49 → pyplumio-0.5.50}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  16. {pyplumio-0.5.49 → pyplumio-0.5.50}/.github/dependabot.yml +0 -0
  17. {pyplumio-0.5.49 → pyplumio-0.5.50}/.github/workflows/ci.yml +0 -0
  18. {pyplumio-0.5.49 → pyplumio-0.5.50}/.github/workflows/codeql.yml +0 -0
  19. {pyplumio-0.5.49 → pyplumio-0.5.50}/.github/workflows/deploy.yml +0 -0
  20. {pyplumio-0.5.49 → pyplumio-0.5.50}/.github/workflows/documentation.yml +0 -0
  21. {pyplumio-0.5.49 → pyplumio-0.5.50}/.gitignore +0 -0
  22. {pyplumio-0.5.49 → pyplumio-0.5.50}/.pre-commit-config.yaml +0 -0
  23. {pyplumio-0.5.49 → pyplumio-0.5.50}/.qlty/qlty.toml +0 -0
  24. {pyplumio-0.5.49 → pyplumio-0.5.50}/.vscode/settings.json +0 -0
  25. {pyplumio-0.5.49 → pyplumio-0.5.50}/LICENSE +0 -0
  26. {pyplumio-0.5.49 → pyplumio-0.5.50}/MANIFEST.in +0 -0
  27. {pyplumio-0.5.49 → pyplumio-0.5.50}/PyPlumIO.egg-info/SOURCES.txt +0 -0
  28. {pyplumio-0.5.49 → pyplumio-0.5.50}/PyPlumIO.egg-info/dependency_links.txt +0 -0
  29. {pyplumio-0.5.49 → pyplumio-0.5.50}/PyPlumIO.egg-info/requires.txt +0 -0
  30. {pyplumio-0.5.49 → pyplumio-0.5.50}/PyPlumIO.egg-info/top_level.txt +0 -0
  31. {pyplumio-0.5.49 → pyplumio-0.5.50}/README.md +0 -0
  32. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/Makefile +0 -0
  33. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/make.bat +0 -0
  34. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/callbacks.rst +0 -0
  35. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/conf.py +0 -0
  36. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/connecting.rst +0 -0
  37. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/frames.rst +0 -0
  38. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/index.rst +0 -0
  39. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/mixers_thermostats.rst +0 -0
  40. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/protocol.rst +0 -0
  41. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/reading.rst +0 -0
  42. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/schedules.rst +0 -0
  43. {pyplumio-0.5.49 → pyplumio-0.5.50}/docs/source/writing.rst +0 -0
  44. {pyplumio-0.5.49 → pyplumio-0.5.50}/images/ecomax.png +0 -0
  45. {pyplumio-0.5.49 → pyplumio-0.5.50}/images/rs485.png +0 -0
  46. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/__init__.py +0 -0
  47. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/__main__.py +0 -0
  48. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/connection.py +0 -0
  49. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/data_types.py +0 -0
  50. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/devices/ecoster.py +0 -0
  51. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/exceptions.py +0 -0
  52. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/filters.py +0 -0
  53. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/frames/__init__.py +0 -0
  54. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/frames/messages.py +0 -0
  55. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/frames/requests.py +0 -0
  56. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/frames/responses.py +0 -0
  57. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/helpers/__init__.py +0 -0
  58. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/helpers/async_cache.py +0 -0
  59. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/helpers/event_manager.py +0 -0
  60. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/helpers/factory.py +0 -0
  61. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/helpers/schedule.py +0 -0
  62. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/helpers/task_manager.py +0 -0
  63. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/helpers/timeout.py +0 -0
  64. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/helpers/uid.py +0 -0
  65. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/parameters/__init__.py +0 -0
  66. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/parameters/custom/__init__.py +0 -0
  67. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/parameters/custom/ecomax_860d3_hb.py +0 -0
  68. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/parameters/ecomax.py +0 -0
  69. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/parameters/mixer.py +0 -0
  70. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/parameters/thermostat.py +0 -0
  71. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/py.typed +0 -0
  72. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/stream.py +0 -0
  73. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/__init__.py +0 -0
  74. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/alerts.py +0 -0
  75. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/boiler_load.py +0 -0
  76. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/boiler_power.py +0 -0
  77. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/ecomax_parameters.py +0 -0
  78. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/fan_power.py +0 -0
  79. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/frame_versions.py +0 -0
  80. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/fuel_consumption.py +0 -0
  81. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/fuel_level.py +0 -0
  82. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/lambda_sensor.py +0 -0
  83. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/mixer_parameters.py +0 -0
  84. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/mixer_sensors.py +0 -0
  85. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/modules.py +0 -0
  86. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/network_info.py +0 -0
  87. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/output_flags.py +0 -0
  88. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/outputs.py +0 -0
  89. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/pending_alerts.py +0 -0
  90. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/product_info.py +0 -0
  91. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/program_version.py +0 -0
  92. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/regulator_data.py +0 -0
  93. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/regulator_data_schema.py +0 -0
  94. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/schedules.py +0 -0
  95. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/statuses.py +0 -0
  96. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/temperatures.py +0 -0
  97. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/thermostat_parameters.py +0 -0
  98. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/structures/thermostat_sensors.py +0 -0
  99. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyplumio/utils.py +0 -0
  100. {pyplumio-0.5.49 → pyplumio-0.5.50}/pyproject.toml +0 -0
  101. {pyplumio-0.5.49 → pyplumio-0.5.50}/requirements.txt +0 -0
  102. {pyplumio-0.5.49 → pyplumio-0.5.50}/requirements_docs.txt +0 -0
  103. {pyplumio-0.5.49 → pyplumio-0.5.50}/requirements_test.txt +0 -0
  104. {pyplumio-0.5.49 → pyplumio-0.5.50}/setup.cfg +0 -0
  105. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/__init__.py +0 -0
  106. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/conftest.py +0 -0
  107. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/devices/__init__.py +0 -0
  108. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/devices/test_ecoster.py +0 -0
  109. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/devices/test_mixer.py +0 -0
  110. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/devices/test_thermostat.py +0 -0
  111. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/frames/test_init.py +0 -0
  112. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/frames/test_messages.py +0 -0
  113. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/frames/test_requests.py +0 -0
  114. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/frames/test_responses.py +0 -0
  115. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/helpers/__init__.py +0 -0
  116. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/helpers/test_async_cache.py +0 -0
  117. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/helpers/test_event_manager.py +0 -0
  118. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/helpers/test_factory.py +0 -0
  119. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/helpers/test_schedule.py +0 -0
  120. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/helpers/test_task_manager.py +0 -0
  121. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/helpers/test_timeout.py +0 -0
  122. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/helpers/test_uid.py +0 -0
  123. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/parameters/__init__.py +0 -0
  124. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/parameters/custom/__init__.py +0 -0
  125. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/parameters/custom/test_ecomax_860d3_hb.py +0 -0
  126. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/parameters/custom/test_init.py +0 -0
  127. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/parameters/test_ecomax.py +0 -0
  128. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/parameters/test_init.py +0 -0
  129. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/parameters/test_mixers.py +0 -0
  130. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/parameters/test_thermostats.py +0 -0
  131. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/ruff.toml +0 -0
  132. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/test_connection.py +0 -0
  133. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/test_data_types.py +0 -0
  134. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/test_filters.py +0 -0
  135. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/test_init.py +0 -0
  136. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/test_main.py +0 -0
  137. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/test_protocol.py +0 -0
  138. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/test_stream.py +0 -0
  139. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/test_utils.py +0 -0
  140. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/messages/regulator_data.json +0 -0
  141. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/messages/sensor_data.json +0 -0
  142. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/parameters/ecomax_860d3_hb.json +0 -0
  143. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/alerts.json +0 -0
  144. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/ecomax_control.json +0 -0
  145. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/ecomax_parameters.json +0 -0
  146. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/mixer_parameters.json +0 -0
  147. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
  148. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/set_mixer_parameter.json +0 -0
  149. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/set_schedule.json +0 -0
  150. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
  151. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/requests/thermostat_parameters.json +0 -0
  152. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/alerts.json +0 -0
  153. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/device_available.json +0 -0
  154. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/ecomax_parameters.json +0 -0
  155. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/mixer_parameters.json +0 -0
  156. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/password.json +0 -0
  157. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/program_version.json +0 -0
  158. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/regulator_data_schema.json +0 -0
  159. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/schedules.json +0 -0
  160. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/thermostat_parameters.json +0 -0
  161. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/responses/uid.json +0 -0
  162. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -0
  163. {pyplumio-0.5.49 → pyplumio-0.5.50}/tests/testdata/unknown/unknown_mixer_parameter.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPlumIO
3
- Version: 0.5.49
3
+ Version: 0.5.50
4
4
  Summary: PyPlumIO is a native ecoNET library for Plum ecoMAX controllers.
5
5
  Author-email: Denis Paavilainen <denpa@denpa.pro>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPlumIO
3
- Version: 0.5.49
3
+ Version: 0.5.50
4
4
  Summary: PyPlumIO is a native ecoNET library for Plum ecoMAX controllers.
5
5
  Author-email: Denis Paavilainen <denpa@denpa.pro>
6
6
  License: MIT License
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.5.49'
21
- __version_tuple__ = version_tuple = (0, 5, 49)
20
+ __version__ = version = '0.5.50'
21
+ __version_tuple__ = version_tuple = (0, 5, 50)
@@ -184,6 +184,7 @@ class FrameType(IntEnum):
184
184
  REQUEST_SET_MIXER_PARAMETER = 52
185
185
  REQUEST_SCHEDULES = 54
186
186
  REQUEST_SET_SCHEDULE = 55
187
+ REQUEST_ECOMAX_PARAMETER_CHANGES = 56
187
188
  REQUEST_UID = 57
188
189
  REQUEST_PASSWORD = 58
189
190
  REQUEST_ECOMAX_CONTROL = 59
@@ -203,6 +204,7 @@ class FrameType(IntEnum):
203
204
  RESPONSE_ECOMAX_CONTROL = 187
204
205
  RESPONSE_ALERTS = 189
205
206
  RESPONSE_PROGRAM_VERSION = 192
207
+ RESPONSE_ECOMAX_PARAMETER_CHANGES = 212
206
208
  RESPONSE_REGULATOR_DATA_SCHEMA = 213
207
209
  RESPONSE_THERMOSTAT_PARAMETERS = 220
208
210
  RESPONSE_SET_THERMOSTAT_PARAMETER = 221
@@ -145,21 +145,26 @@ class PhysicalDevice(Device, ABC):
145
145
  @event_listener(filter=on_change)
146
146
  async def on_event_frame_versions(self, versions: dict[int, int]) -> None:
147
147
  """Check frame versions and update outdated frames."""
148
- _LOGGER.info("Received frame version table")
148
+ _LOGGER.debug("Received frame version table")
149
149
  for frame_type, version in versions.items():
150
150
  if (
151
151
  is_known_frame_type(frame_type)
152
152
  and self.supports_frame_type(frame_type)
153
153
  and not self.has_frame_version(frame_type, version)
154
154
  ):
155
- await self._request_frame_version(frame_type, version)
155
+ request_frame_type = (
156
+ FrameType.REQUEST_ECOMAX_PARAMETERS
157
+ if frame_type == FrameType.REQUEST_ECOMAX_PARAMETER_CHANGES
158
+ else frame_type
159
+ )
160
+ await self._request_frame_version(request_frame_type, version)
156
161
  self._frame_versions[frame_type] = version
157
162
 
158
163
  async def _request_frame_version(
159
164
  self, frame_type: FrameType | int, version: int
160
165
  ) -> None:
161
166
  """Request frame version from the device."""
162
- _LOGGER.info("Updating frame %s to version %i", repr(frame_type), version)
167
+ _LOGGER.debug("Updating frame %s to version %i", repr(frame_type), version)
163
168
  request = await Request.create(frame_type, recipient=self.address)
164
169
  self.queue.put_nowait(request)
165
170
 
@@ -234,7 +234,7 @@ class EcoMAX(PhysicalDevice):
234
234
  @event_listener
235
235
  async def on_event_setup(self, setup: bool) -> None:
236
236
  """Request frames required to set up an ecoMAX entry."""
237
- _LOGGER.info("Setting up device entry")
237
+ _LOGGER.debug("Setting up device entry")
238
238
  await self.wait_for(ATTR_SENSORS)
239
239
  results = await asyncio.gather(
240
240
  *(
@@ -251,14 +251,14 @@ class EcoMAX(PhysicalDevice):
251
251
  if errors:
252
252
  self.dispatch_nowait(ATTR_FRAME_ERRORS, errors)
253
253
 
254
- _LOGGER.info("Device entry setup done")
254
+ _LOGGER.debug("Device entry setup done")
255
255
 
256
256
  @event_listener
257
257
  async def on_event_ecomax_parameters(
258
258
  self, parameters: list[tuple[int, ParameterValues]]
259
259
  ) -> bool:
260
260
  """Update ecoMAX parameters and dispatch the events."""
261
- _LOGGER.info("Received device parameters")
261
+ _LOGGER.debug("Received device parameters")
262
262
  product_info: ProductInfo = await self.get(ATTR_PRODUCT)
263
263
  parameter_types = await get_ecomax_parameter_types(product_info)
264
264
 
@@ -307,7 +307,7 @@ class EcoMAX(PhysicalDevice):
307
307
  parameters: dict[int, list[tuple[int, ParameterValues]]] | None,
308
308
  ) -> bool:
309
309
  """Handle mixer parameters and dispatch the events."""
310
- _LOGGER.info("Received mixer parameters")
310
+ _LOGGER.debug("Received mixer parameters")
311
311
  if parameters:
312
312
  await asyncio.gather(
313
313
  *(
@@ -324,7 +324,7 @@ class EcoMAX(PhysicalDevice):
324
324
  self, sensors: dict[int, dict[str, Any]] | None
325
325
  ) -> bool:
326
326
  """Update mixer sensors and dispatch the events."""
327
- _LOGGER.info("Received mixer sensors")
327
+ _LOGGER.debug("Received mixer sensors")
328
328
  if sensors:
329
329
  await asyncio.gather(
330
330
  *(
@@ -364,7 +364,7 @@ class EcoMAX(PhysicalDevice):
364
364
  @event_listener
365
365
  async def on_event_sensors(self, sensors: dict[str, Any]) -> bool:
366
366
  """Update ecoMAX sensors and dispatch the events."""
367
- _LOGGER.info("Received device sensors")
367
+ _LOGGER.debug("Received device sensors")
368
368
  await asyncio.gather(
369
369
  *(self.dispatch(name, value) for name, value in sensors.items())
370
370
  )
@@ -376,7 +376,7 @@ class EcoMAX(PhysicalDevice):
376
376
  parameters: dict[int, list[tuple[int, ParameterValues]]] | None,
377
377
  ) -> bool:
378
378
  """Handle thermostat parameters and dispatch the events."""
379
- _LOGGER.info("Received thermostat parameters")
379
+ _LOGGER.debug("Received thermostat parameters")
380
380
  if parameters:
381
381
  await asyncio.gather(
382
382
  *(
@@ -407,7 +407,7 @@ class EcoMAX(PhysicalDevice):
407
407
  self, sensors: dict[int, dict[str, Any]] | None
408
408
  ) -> bool:
409
409
  """Update thermostat sensors and dispatch the events."""
410
- _LOGGER.info("Received thermostat sensors")
410
+ _LOGGER.debug("Received thermostat sensors")
411
411
  if sensors:
412
412
  await asyncio.gather(
413
413
  *(
@@ -427,7 +427,7 @@ class EcoMAX(PhysicalDevice):
427
427
  self, schedules: list[tuple[int, list[list[bool]]]]
428
428
  ) -> dict[str, Schedule]:
429
429
  """Update schedules."""
430
- _LOGGER.info("Received device schedules")
430
+ _LOGGER.debug("Received device schedules")
431
431
  return {
432
432
  SCHEDULES[index]: Schedule(
433
433
  name=SCHEDULES[index],
@@ -29,7 +29,7 @@ class Mixer(VirtualDevice):
29
29
  @event_listener
30
30
  async def on_event_mixer_sensors(self, sensors: dict[str, Any]) -> bool:
31
31
  """Update mixer sensors and dispatch the events."""
32
- _LOGGER.info("Received mixer %i sensors", self.index)
32
+ _LOGGER.debug("Received mixer %i sensors", self.index)
33
33
  await asyncio.gather(
34
34
  *(self.dispatch(name, value) for name, value in sensors.items())
35
35
  )
@@ -40,7 +40,7 @@ class Mixer(VirtualDevice):
40
40
  self, parameters: list[tuple[int, ParameterValues]]
41
41
  ) -> bool:
42
42
  """Update mixer parameters and dispatch the events."""
43
- _LOGGER.info("Received mixer %i parameters", self.index)
43
+ _LOGGER.debug("Received mixer %i parameters", self.index)
44
44
  product_info: ProductInfo = await self.parent.get(ATTR_PRODUCT)
45
45
  parameter_types = get_mixer_parameter_types(product_info)
46
46
 
@@ -28,7 +28,7 @@ class Thermostat(VirtualDevice):
28
28
  @event_listener
29
29
  async def on_event_thermostat_sensors(self, sensors: dict[str, Any]) -> bool:
30
30
  """Update thermostat sensors and dispatch the events."""
31
- _LOGGER.info("Received thermostat %i sensors", self.index)
31
+ _LOGGER.debug("Received thermostat %i sensors", self.index)
32
32
  await asyncio.gather(
33
33
  *(self.dispatch(name, value) for name, value in sensors.items())
34
34
  )
@@ -39,7 +39,7 @@ class Thermostat(VirtualDevice):
39
39
  self, parameters: list[tuple[int, ParameterValues]]
40
40
  ) -> bool:
41
41
  """Update thermostat parameters and dispatch the events."""
42
- _LOGGER.info("Received thermostat %i parameters", self.index)
42
+ _LOGGER.debug("Received thermostat %i parameters", self.index)
43
43
  parameter_types = get_thermostat_parameter_types()
44
44
 
45
45
  def _thermostat_parameter_events() -> Generator[Coroutine]:
@@ -230,20 +230,17 @@ class AsyncProtocol(Protocol, EventManager[PhysicalDevice]):
230
230
  @acache
231
231
  async def get_device_entry(self, device_type: DeviceType) -> PhysicalDevice:
232
232
  """Return the device entry."""
233
-
234
- @acache
235
- async def _setup_device_entry(device_type: DeviceType) -> PhysicalDevice:
236
- """Set up the device entry."""
237
- device = await PhysicalDevice.create(
238
- device_type, queue=self._queues.write, network=self._network
239
- )
240
- device.dispatch_nowait(ATTR_CONNECTED, True)
241
- device.dispatch_nowait(ATTR_SETUP, True)
242
- self.dispatch_nowait(device_type.name.lower(), device)
243
- return device
244
-
245
233
  async with self._entry_lock:
246
- return await _setup_device_entry(device_type)
234
+ name = device_type.name.lower()
235
+ if name not in self.data:
236
+ device = await PhysicalDevice.create(
237
+ device_type, queue=self._queues.write, network=self._network
238
+ )
239
+ device.dispatch_nowait(ATTR_CONNECTED, True)
240
+ device.dispatch_nowait(ATTR_SETUP, True)
241
+ await self.dispatch(device_type.name.lower(), device)
242
+
243
+ return self.data[name]
247
244
 
248
245
 
249
246
  __all__ = ["Protocol", "DummyProtocol", "AsyncProtocol"]
@@ -43,6 +43,7 @@ from pyplumio.frames.messages import SensorDataMessage
43
43
  from pyplumio.frames.requests import (
44
44
  AlertsRequest,
45
45
  EcomaxControlRequest,
46
+ EcomaxParametersRequest,
46
47
  SetEcomaxParameterRequest,
47
48
  SetScheduleRequest,
48
49
  SetThermostatParameterRequest,
@@ -104,10 +105,24 @@ async def test_ecomax_handle_frame(
104
105
  mock_handle_frame.assert_called_once_with(request)
105
106
 
106
107
 
108
+ @pytest.mark.parametrize(
109
+ ("frame_type", "frame_request"),
110
+ [
111
+ (FrameType.REQUEST_ALERTS, AlertsRequest(recipient=DeviceType.ECOMAX)),
112
+ (
113
+ FrameType.REQUEST_ECOMAX_PARAMETER_CHANGES,
114
+ EcomaxParametersRequest(recipient=DeviceType.ECOMAX),
115
+ ),
116
+ ],
117
+ )
107
118
  @patch("asyncio.Queue.put_nowait")
108
119
  @class_from_json(SensorDataMessage, "messages/sensor_data.json", arguments=("message",))
109
120
  async def test_frame_versions_tracker(
110
- mock_put_nowait, ecomax: EcoMAX, sensor_data: SensorDataMessage
121
+ mock_put_nowait,
122
+ ecomax: EcoMAX,
123
+ sensor_data: SensorDataMessage,
124
+ frame_type: FrameType,
125
+ frame_request: Request,
111
126
  ) -> None:
112
127
  """Test frame version tracker."""
113
128
 
@@ -116,7 +131,7 @@ async def test_frame_versions_tracker(
116
131
  return {ATTR_SENSORS: {ATTR_FRAME_VERSIONS: {frame: version}}}
117
132
 
118
133
  # Test with frame type that are handled during setup.
119
- sensor_data.data = _frame_version_data(FrameType.REQUEST_ALERTS, 1)
134
+ sensor_data.data = _frame_version_data(frame_type, 1)
120
135
  ecomax.data.clear()
121
136
  ecomax.handle_frame(sensor_data)
122
137
  await ecomax.wait_until_done()
@@ -124,12 +139,12 @@ async def test_frame_versions_tracker(
124
139
 
125
140
  # Test with same frame type after setup done.
126
141
  mock_put_nowait.reset_mock()
127
- sensor_data.data = _frame_version_data(FrameType.REQUEST_ALERTS, 2)
142
+ sensor_data.data = _frame_version_data(frame_type, 2)
128
143
  ecomax.data.clear()
129
144
  ecomax.data[ATTR_SETUP] = True
130
145
  ecomax.handle_frame(sensor_data)
131
146
  await ecomax.wait_until_done()
132
- mock_put_nowait.assert_called_once_with(AlertsRequest(recipient=DeviceType.ECOMAX))
147
+ mock_put_nowait.assert_called_once_with(frame_request)
133
148
 
134
149
 
135
150
  @pytest.mark.parametrize("state", [STATE_ON, STATE_OFF])
@@ -102,19 +102,34 @@ def fixture_physical_device() -> PhysicalDevice:
102
102
  class TestPhysicalDevice:
103
103
  """Contains tests for PhysicalDevice class."""
104
104
 
105
+ @pytest.mark.parametrize(
106
+ ("frame_type", "requested_frame_type"),
107
+ [
108
+ (FrameType.REQUEST_ALERTS, FrameType.REQUEST_ALERTS),
109
+ (
110
+ FrameType.REQUEST_ECOMAX_PARAMETER_CHANGES,
111
+ FrameType.REQUEST_ECOMAX_PARAMETERS,
112
+ ),
113
+ ],
114
+ )
105
115
  @patch("pyplumio.frames.Request.create", autospec=True)
106
116
  @patch("asyncio.Queue.put_nowait")
107
117
  async def test_frame_versions_event_listener(
108
- self, mock_put_nowait, mock_request_create, physical_device: PhysicalDevice
118
+ self,
119
+ mock_put_nowait,
120
+ mock_request_create,
121
+ physical_device: PhysicalDevice,
122
+ frame_type: FrameType,
123
+ requested_frame_type: FrameType,
109
124
  ) -> None:
110
125
  """Test event listener for frame versions."""
111
- assert physical_device.has_frame_version(FrameType.REQUEST_ALERTS, 1) is False
112
- await physical_device.on_event_frame_versions({FrameType.REQUEST_ALERTS: 1})
126
+ assert physical_device.has_frame_version(frame_type, 1) is False
127
+ await physical_device.on_event_frame_versions({frame_type: 1})
113
128
  mock_request_create.assert_awaited_once_with(
114
- FrameType.REQUEST_ALERTS, recipient=DummyPhysicalDevice.address
129
+ requested_frame_type, recipient=DummyPhysicalDevice.address
115
130
  )
116
131
  mock_put_nowait.assert_called_once_with(mock_request_create.return_value)
117
- assert physical_device.has_frame_version(FrameType.REQUEST_ALERTS, 1) is True
132
+ assert physical_device.has_frame_version(frame_type, 1) is True
118
133
 
119
134
  def test_frame_versions_event_listener_decorator(self) -> None:
120
135
  """Test decorator for the frame version event listener."""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes