ramses-rf 0.53.1__tar.gz → 0.53.3__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 (390) hide show
  1. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/PKG-INFO +1 -1
  2. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/binding_process.md +2 -1
  3. ramses_rf-0.53.3/docs/source/modules.rst +97 -0
  4. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/ramses_rf.rst +14 -0
  5. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/database.py +113 -61
  6. ramses_rf-0.53.3/ramses_rf/storage.py +168 -0
  7. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/version.py +1 -1
  8. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/parsers.py +3 -1
  9. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/ramses.py +1 -1
  10. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/transport.py +26 -26
  11. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/version.py +1 -1
  12. ramses_rf-0.53.3/requirements/requirements_docs.txt +15 -0
  13. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_31da.log +7 -0
  14. ramses_rf-0.53.3/tests/tests/test_storage.py +115 -0
  15. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_database.py +5 -4
  16. ramses_rf-0.53.3/tests/tests_rf/test_virtual_rf.py +153 -0
  17. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/virtual_rf/__init__.py +47 -29
  18. ramses_rf-0.53.3/tests/tests_rf/virtual_rf/const.py +114 -0
  19. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/virtual_rf/virtual_rf.py +217 -199
  20. ramses_rf-0.53.1/docs/source/modules.rst +0 -9
  21. ramses_rf-0.53.1/requirements/requirements_docs.txt +0 -17
  22. ramses_rf-0.53.1/tests/tests_rf/virtual_rf/const.py +0 -91
  23. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  24. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.github/dependabot.yml +0 -0
  25. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.github/workflows/check-cov.yml +0 -0
  26. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.github/workflows/check-lint.yml +0 -0
  27. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.github/workflows/check-test.yml +0 -0
  28. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.github/workflows/check-type.yml +0 -0
  29. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.github/workflows/publish-hatch.yml +0 -0
  30. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.gitignore +0 -0
  31. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/.pre-commit-config.yaml +0 -0
  32. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/LICENSE +0 -0
  33. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/README-developers.md +0 -0
  34. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/README.md +0 -0
  35. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/client.py +0 -0
  36. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/Makefile +0 -0
  37. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/make.bat +0 -0
  38. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/_static/CC-BY-NC-SA-4.0.txt +0 -0
  39. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/_static/ramses_rf_logo.png +0 -0
  40. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/conf.py +0 -0
  41. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/glossary.rst +0 -0
  42. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/index.rst +0 -0
  43. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/ramses_cli.rst +0 -0
  44. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/ramses_rf.device.rst +0 -0
  45. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/ramses_rf.system.rst +0 -0
  46. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/ramses_tx.rst +0 -0
  47. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/docs/source/usage.md +0 -0
  48. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/misc/2411_parser.py +0 -0
  49. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/misc/fingerprints.log +0 -0
  50. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/misc/ser2net.yaml +0 -0
  51. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/misc/ti_3410/notes.sh +0 -0
  52. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/misc/ti_3410/ti_3410.fw +0 -0
  53. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/pyproject.toml +0 -0
  54. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_cli/__init__.py +0 -0
  55. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_cli/client.py +0 -0
  56. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_cli/debug.py +0 -0
  57. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_cli/discovery.py +0 -0
  58. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_cli/py.typed +0 -0
  59. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_cli/utils/cat_slow.py +0 -0
  60. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_cli/utils/convert.py +0 -0
  61. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/__init__.py +0 -0
  62. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/binding_fsm.py +0 -0
  63. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/const.py +0 -0
  64. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/device/__init__.py +0 -0
  65. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/device/base.py +0 -0
  66. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/device/heat.py +0 -0
  67. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/device/hvac.py +0 -0
  68. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/dispatcher.py +0 -0
  69. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/entity_base.py +0 -0
  70. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/exceptions.py +0 -0
  71. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/gateway.py +0 -0
  72. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/helpers.py +0 -0
  73. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/py.typed +0 -0
  74. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/schemas.py +0 -0
  75. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/system/__init__.py +0 -0
  76. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/system/faultlog.py +0 -0
  77. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/system/heat.py +0 -0
  78. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/system/schedule.py +0 -0
  79. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_rf/system/zones.py +0 -0
  80. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/__init__.py +0 -0
  81. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/address.py +0 -0
  82. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/command.py +0 -0
  83. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/const.py +0 -0
  84. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/exceptions.py +0 -0
  85. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/fingerprints.py +0 -0
  86. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/frame.py +0 -0
  87. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/gateway.py +0 -0
  88. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/helpers.py +0 -0
  89. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/logger.py +0 -0
  90. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/message.py +0 -0
  91. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/opentherm.py +0 -0
  92. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/packet.py +0 -0
  93. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/protocol.py +0 -0
  94. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/protocol_fsm.py +0 -0
  95. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/py.typed +0 -0
  96. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/schemas.py +0 -0
  97. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/typed_dicts.py +0 -0
  98. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/ramses_tx/typing.py +0 -0
  99. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/requirements/requirements.txt +0 -0
  100. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/requirements/requirements_dev.txt +0 -0
  101. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/_test_apis_mock.py +0 -0
  102. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/_test_mock_faultlog.py +0 -0
  103. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/_test_mock_schedule.py +0 -0
  104. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/_test_packets_bad.py +0 -0
  105. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/_test_performance_WIP.py +0 -0
  106. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/_test_state_mgt.py +0 -0
  107. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/common.py +0 -0
  108. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/mocked_devices/__init__.py +0 -0
  109. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/mocked_devices/command.py +0 -0
  110. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/mocked_devices/const.py +0 -0
  111. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/mocked_devices/device_heat.py +0 -0
  112. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/mocked_devices/device_hvac.py +0 -0
  113. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/deprecated/mocked_devices/transport.py +0 -0
  114. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/test_HA_MQTT/test_transport_callback.py +0 -0
  115. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/__init__.py +0 -0
  116. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/heat/ctl_bdr_91t.log +0 -0
  117. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/heat/dts_ctl_sensor.log +0 -0
  118. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/heat/hcw_ctl_sensor.log +0 -0
  119. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/heat/rnd_ctl_sensor.log +0 -0
  120. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/heat/rnd_ctl_sensor.yaml +0 -0
  121. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/heat/trv_ctl.log +0 -0
  122. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/co2_fan_itho.json +0 -0
  123. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/co2_fan_itho.log +0 -0
  124. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/co2_fan_itho.yaml +0 -0
  125. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/dis_fan_orcon.json +0 -0
  126. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/dis_fan_orcon.log +0 -0
  127. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/rem_fan_climarad.log +0 -0
  128. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/rem_fan_nuaire.json +0 -0
  129. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/rem_fan_nuaire.log +0 -0
  130. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/rem_fan_nuaire.yaml +0 -0
  131. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/rem_fan_vasco.log +0 -0
  132. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/bindings/hvac/rem_fan_ventura.log +0 -0
  133. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/devices/device_02.log +0 -0
  134. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/devices/device_04.log +0 -0
  135. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/devices/device_10.log +0 -0
  136. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/devices/device_13.log +0 -0
  137. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/devices/device_22.log +0 -0
  138. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_dev_class/hvac/known_list_eavesdrop_off.json +0 -0
  139. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_dev_class/hvac/known_list_eavesdrop_on.json +0 -0
  140. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_dev_class/hvac/packet.log +0 -0
  141. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_trv_actuator_long/packet.log +0 -0
  142. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_trv_actuator_long/schema_eavesdrop_off.json +0 -0
  143. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_trv_actuator_long/schema_eavesdrop_on.json +0 -0
  144. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_trv_actuator_mixed/packet.log +0 -0
  145. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_trv_actuator_mixed/schema_eavesdrop_off.json +0 -0
  146. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_trv_actuator_mixed/schema_eavesdrop_on.json +0 -0
  147. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_ufh_circuits/packet.log +0 -0
  148. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_ufh_circuits/schema_eavesdrop_off.json +0 -0
  149. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/_ufh_circuits/schema_eavesdrop_on.json +0 -0
  150. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/app_cntrl/packet.log +0 -0
  151. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/app_cntrl/schema_eavesdrop_off.json +0 -0
  152. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/app_cntrl/schema_eavesdrop_on.json +0 -0
  153. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/trv_actuators/packet.log +0 -0
  154. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/trv_actuators/schema_eavesdrop_off.json +0 -0
  155. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/trv_actuators/schema_eavesdrop_on.json +0 -0
  156. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_000/packet.log +0 -0
  157. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_000/schema_eavesdrop_off.json +0 -0
  158. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_000/schema_eavesdrop_on.json +0 -0
  159. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_001/packet.log +0 -0
  160. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_001/schema_eavesdrop_off.json +0 -0
  161. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_001/schema_eavesdrop_on.json +0 -0
  162. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_002/packet.log +0 -0
  163. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_002/schema_eavesdrop_off.json +0 -0
  164. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_002/schema_eavesdrop_on.json +0 -0
  165. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_003/packet.log +0 -0
  166. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_003/schema_eavesdrop_off.json +0 -0
  167. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_003/schema_eavesdrop_on.json +0 -0
  168. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_004/packet.log +0 -0
  169. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_004/schema_eavesdrop_off.json +0 -0
  170. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/eavesdrop_schema/zone_sensors_004/schema_eavesdrop_on.json +0 -0
  171. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/10e0_xxxx.log +0 -0
  172. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/_gather.sh +0 -0
  173. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/01_EvoTouch_Colour.log +0 -0
  174. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/01_Evo_Color.log +0 -0
  175. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/01_IONA_RAI_Prototype.log +0 -0
  176. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/02_HCE80_V3.10_061117..log +0 -0
  177. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/04_HR92 Radiator Ctrl_.log +0 -0
  178. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/08_Jasper_EIM.log +0 -0
  179. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/10_R8810A_Bridge.log +0 -0
  180. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/10_R8820.log +0 -0
  181. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/30_Internet_Gateway.log +0 -0
  182. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/31_Jasper_Stat_TXXX.log +0 -0
  183. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/heat/34_T87RF2025.log +0 -0
  184. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/18_BRDG-02A55.log +0 -0
  185. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/18_HRA82.log +0 -0
  186. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/20.log +0 -0
  187. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/21_CCU-12T20.log +0 -0
  188. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/29_VMC-07RP01.log +0 -0
  189. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/29_VMC-15RP01.log +0 -0
  190. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/29_VMC-17RP01.log +0 -0
  191. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/29_VMN-07LM01.log +0 -0
  192. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/29_VMN-15LF01.log +0 -0
  193. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/29_VMN-17LMP01.log +0 -0
  194. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/29_VMS-15C16.log +0 -0
  195. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/29_VMS-17HB01.log +0 -0
  196. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/30_BRDG-02EM23.log +0 -0
  197. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/30_BRDG-02JAS01.log +0 -0
  198. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMC-15RPS34.log +0 -0
  199. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMD-15RMS64.log +0 -0
  200. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMD-15RMS86.log +0 -0
  201. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMD-17RPS01.log +0 -0
  202. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMN-23LM33.log +0 -0
  203. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMN-23LMH23.log +0 -0
  204. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMS-15CM17.log +0 -0
  205. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMS-23C33.log +0 -0
  206. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMS-23HB33.log +0 -0
  207. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/32_VMZ-15V13.log +0 -0
  208. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/37_VMD-07RPS13.log +0 -0
  209. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/37_VMI-15MC01log +0 -0
  210. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/37_VMI-15WSJ53.log +0 -0
  211. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/37_VMS-02J52.log +0 -0
  212. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/37_VMS-12C39.log +0 -0
  213. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/99_CVE-RF.log +0 -0
  214. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/fingerprints/hvac/99_VMS-17C01.log +0 -0
  215. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/helpers.py +0 -0
  216. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/logger/packet_in.log +0 -0
  217. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/logger/packet_out.log +0 -0
  218. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/logs/pkts_bad_000.log +0 -0
  219. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/logs/pkts_tba_000.log +0 -0
  220. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/logs/system_cache.json +0 -0
  221. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parser_helpers/pkt_addr_set.log +0 -0
  222. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parser_helpers/pkt_dev_class.log +0 -0
  223. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_0001_wip.log +0 -0
  224. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_0002.log +0 -0
  225. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_0004_wip.log +0 -0
  226. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_0005.log +0 -0
  227. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_0006.log +0 -0
  228. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_0008.log +0 -0
  229. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_0009.log +0 -0
  230. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_000a.log +0 -0
  231. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_000c.log +0 -0
  232. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_000e.log +0 -0
  233. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_01ff_wip.log +0 -0
  234. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_0418.log +0 -0
  235. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_042f.log +0 -0
  236. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_1030.log +0 -0
  237. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_1060.log +0 -0
  238. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_10d0.log +0 -0
  239. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_10e0.log +0 -0
  240. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_1260.log +0 -0
  241. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_1298.log +0 -0
  242. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_12a0.log +0 -0
  243. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_12c0.log +0 -0
  244. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_1300.log +0 -0
  245. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_1f09.log +0 -0
  246. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_1fc9.log +0 -0
  247. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_1fd4.log +0 -0
  248. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_2210.log +0 -0
  249. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22c9.log +0 -0
  250. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22d0.log +0 -0
  251. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22d9.log +0 -0
  252. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22e0.log +0 -0
  253. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22e5.log +0 -0
  254. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22e9.log +0 -0
  255. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22f1.log +0 -0
  256. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22f2.log +0 -0
  257. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22f3.log +0 -0
  258. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22f4.log +0 -0
  259. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_22f7.log +0 -0
  260. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_2309.log +0 -0
  261. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_2349.log +0 -0
  262. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_2411_wip.log +0 -0
  263. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_2e04.log +0 -0
  264. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_2e10_wip.log +0 -0
  265. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_30c9.log +0 -0
  266. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3110_wip.log +0 -0
  267. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3120.log +0 -0
  268. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_313e_wip.log +0 -0
  269. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3150.log +0 -0
  270. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_31d9.log +0 -0
  271. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3200.log +0 -0
  272. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3210.log +0 -0
  273. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3220.log +0 -0
  274. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3222.log +0 -0
  275. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3ef0_wip.log +0 -0
  276. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_3ef1_wip.log +0 -0
  277. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_4e01.log +0 -0
  278. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_4e02.log +0 -0
  279. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_4e04.log +0 -0
  280. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/parsers/code_4e15.log +0 -0
  281. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schedules/_sched_002/packet.log +0 -0
  282. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schedules/_sched_002/schedule.json +0 -0
  283. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schedules/sched_001/packet.log +0 -0
  284. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schedules/sched_001/schedule.json +0 -0
  285. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schedules/sched_dhw/packet.log +0 -0
  286. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schedules/sched_dhw/schedule.json +0 -0
  287. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/jsn_files/schema_100.json +0 -0
  288. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/jsn_files/schema_101.json +0 -0
  289. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/jsn_files/schema_102.json +0 -0
  290. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/jsn_files/schema_103.json +0 -0
  291. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/jsn_files/schema_104.json +0 -0
  292. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/jsn_files/schema_105.json +0 -0
  293. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/jsn_files/schema_108.json +0 -0
  294. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_000.json +0 -0
  295. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_000.log +0 -0
  296. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_001.json +0 -0
  297. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_001.log +0 -0
  298. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_002.json +0 -0
  299. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_002.log +0 -0
  300. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_010.json +0 -0
  301. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_010.log +0 -0
  302. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_011.json +0 -0
  303. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_011.log +0 -0
  304. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_012.json +0 -0
  305. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_012.log +0 -0
  306. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_013.json +0 -0
  307. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_013.log +0 -0
  308. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_014.json +0 -0
  309. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_014.log +0 -0
  310. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_300.json +0 -0
  311. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_300.log +0 -0
  312. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_301.json +0 -0
  313. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_301.log +0 -0
  314. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_302.json +0 -0
  315. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_302.log +0 -0
  316. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_303.json +0 -0
  317. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_303.log +0 -0
  318. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_304.json +0 -0
  319. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_304.log +0 -0
  320. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_310.json +0 -0
  321. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/schemas/log_files/schema_310.log +0 -0
  322. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/_heat_trv_00/config.json +0 -0
  323. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/_heat_trv_00/packet.log +0 -0
  324. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/_heat_trv_00/schema.json +0 -0
  325. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/_hvac_nuaire/config.json +0 -0
  326. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/_hvac_nuaire/known_list.json +0 -0
  327. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/_hvac_nuaire/packet.log +0 -0
  328. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/_hvac_nuaire/schema.json +0 -0
  329. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/_hvac_nuaire/status.json +0 -0
  330. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_otb_00/config.json +0 -0
  331. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_otb_00/packet.log +0 -0
  332. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_otb_00/schema.json +0 -0
  333. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_simple/packet.log +0 -0
  334. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_simple/schema.json +0 -0
  335. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_ufc_00/config.json +0 -0
  336. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_ufc_00/packet.log +0 -0
  337. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_ufc_01/packet.log +0 -0
  338. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_zxdavb/config.json +0 -0
  339. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_zxdavb/known_list.json +0 -0
  340. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_zxdavb/packet.log +0 -0
  341. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/systems/heat_zxdavb/schema.json +0 -0
  342. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_api_faultlog.py +0 -0
  343. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_api_schedule.py +0 -0
  344. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_apis_binding.py +0 -0
  345. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_apis_common.py +0 -0
  346. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_apis_heat.py +0 -0
  347. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_apis_hvac.py +0 -0
  348. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_cli_transport_factory.py +0 -0
  349. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_devices.py +0 -0
  350. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_eavesdrop_dev_class.py +0 -0
  351. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_eavesdrop_schema.py +0 -0
  352. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_helpers.py +0 -0
  353. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_parser_helpers.py +0 -0
  354. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_parsers.py +0 -0
  355. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_ramses_schema.py +0 -0
  356. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_schema_bits.py +0 -0
  357. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_schemas.py +0 -0
  358. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_systems.py +0 -0
  359. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests/test_vol_schemas.py +0 -0
  360. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_cli/test_cli_utility.py +0 -0
  361. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_cli/test_client.py +0 -0
  362. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_cli/test_debug.py +0 -0
  363. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_cli/test_discovery.py +0 -0
  364. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/__init__.py +0 -0
  365. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/configs/config_heat.json +0 -0
  366. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/configs/config_hvac.json +0 -0
  367. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/conftest.py +0 -0
  368. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/device/__init__.py +0 -0
  369. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/device/test_hvac_ventilator.py +0 -0
  370. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/logs/test_api_faultlog.log +0 -0
  371. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_api_faultlog.py +0 -0
  372. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_api_schedule.py +0 -0
  373. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_binding_fsm.py +0 -0
  374. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_create_stack.py +0 -0
  375. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_dispatcher.py +0 -0
  376. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_entity_base.py +0 -0
  377. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_hgi_behaviors.py +0 -0
  378. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_protocol_fsm.py +0 -0
  379. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_use_regex.py +0 -0
  380. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/test_virt_network.py +0 -0
  381. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_rf/virtual_rf/helpers.py +0 -0
  382. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_tx/__init__.py +0 -0
  383. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_tx/test_command.py +0 -0
  384. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/tests_tx/test_const.py +0 -0
  385. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/wip/_test_eavesdrop_dhw_sensor.py +0 -0
  386. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/wip/_test_eavesdrop_htg_control.py +0 -0
  387. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/wip/_test_eavesdrop_ufc_circuits.py +0 -0
  388. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/wip/_test_eavesdrop_zone_sensors.py +0 -0
  389. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/wip/_test_eavesdrop_zone_type.py +0 -0
  390. {ramses_rf-0.53.1 → ramses_rf-0.53.3}/tests/wip/test_wip_cli.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ramses_rf
3
- Version: 0.53.1
3
+ Version: 0.53.3
4
4
  Summary: A stateful RAMSES-II protocol decoder & analyser.
5
5
  Project-URL: Homepage, https://github.com/ramses-rf/ramses_rf
6
6
  Project-URL: Bug Tracker, https://github.com/ramses-rf/ramses_rf/issues
@@ -7,7 +7,8 @@ This diagram shows the binding finite state machine (FSM) for RAMSES RF devices,
7
7
 
8
8
  ```mermaid
9
9
  stateDiagram-v2
10
- direction LR
10
+ direction TB
11
+
11
12
  [*] --> DevIsNotBinding
12
13
  DevIsNotBinding --> InitiateBindingProcess : initiate_binding_process()
13
14
  InitiateBindingProcess --> SuppSendOfferWaitForAccept
@@ -0,0 +1,97 @@
1
+ ramses_rf/src/
2
+ ==============
3
+
4
+ .. mermaid::
5
+
6
+ classDiagram
7
+ direction RL
8
+
9
+ namespace ramses_rf-dispatcher{
10
+ class Dispatcher{
11
+ + create_device()
12
+ + ..
13
+ + process_msg()
14
+ }
15
+ }
16
+ namespace ramses_rf-database{
17
+ class Database {
18
+ - add(msg)
19
+ - ..
20
+ - qry(msg)
21
+ - ..
22
+ - rem(msg)
23
+ }
24
+ }
25
+ namespace ramses_rf-device{
26
+ class heat
27
+ class hvac
28
+ }
29
+ namespace ramses_rf-gateway{
30
+ class hgi
31
+ }
32
+ namespace ramses_tx-message{
33
+ class Message {
34
+ - parse_message()
35
+ - ..
36
+ - parse_payload()
37
+ - ..
38
+ - validate_msg()
39
+ }
40
+ }
41
+ namespace ramses_tx-command{
42
+ class Command{
43
+ - _from_attrs
44
+ - ..
45
+ - set_zone_config
46
+ }
47
+ }
48
+ namespace ramses_tx-gateway-engine{
49
+ class Engine {
50
+ - add_msg_handler()
51
+ - ..
52
+ - create_cmd()
53
+ - ..
54
+ - async_send_cmd()
55
+ }
56
+ }
57
+ namespace ramses_tx-transport{
58
+ class Transport {
59
+ - MqttTransport
60
+ - ..
61
+ - PortTransport
62
+ }
63
+ }
64
+ namespace ramses_ESP{
65
+ class RF:::esp
66
+ class Serial:::esp
67
+ }
68
+
69
+ Transport <|--|> Serial
70
+ Transport <|--|> RF
71
+ Transport <|--|> Engine
72
+ Engine <|--|> hgi
73
+ Database <|--|> hgi
74
+ Message <|--|> hgi
75
+ heat <|--|> hgi
76
+ hvac <|--|> hgi
77
+ Dispatcher <|--|> hvac
78
+ Dispatcher <|--|> heat
79
+ Dispatcher <|--|> Message
80
+
81
+ click Transport href "ramses_tx.html#module-ramses_tx.transport" "docs"
82
+ click Engine href "ramses_tx.html#module-ramses_tx.gateway" "docs"
83
+ click hgi href "ramses_rf.html#module-ramses_rf.gateway" "docs"
84
+ click Database href "ramses_rf.html#module-ramses_rf.database" "docs"
85
+ click Message href "ramses_tx.html#module-ramses_tx.message" "docs"
86
+ click heat href "ramses_rf.device.html#module-ramses_rf.device.heat" "docs"
87
+ click hvac href "ramses_rf.device.html#module-ramses_rf.device.hvac" "docs"
88
+ click Dispatcher href "ramses_rf.html#module-ramses_rf.dispatcher" "docs"
89
+ click Command href "ramses_tx.html#module-ramses_tx.command" "docs"
90
+
91
+ .. toctree::
92
+ :maxdepth: 2
93
+ :caption: Contents:
94
+
95
+ ramses_cli
96
+ ramses_rf
97
+ ramses_tx
@@ -32,6 +32,20 @@ ramses\_rf.const module
32
32
  ramses\_rf.database module
33
33
  --------------------------
34
34
 
35
+ .. mermaid::
36
+ erDiagram
37
+ msg_db ||--o{ device : query
38
+
39
+ msg_db {
40
+ TEXT(26)* dtm
41
+ TEXT(2) verb
42
+ TEXT(9) src
43
+ TEXT(9) dst
44
+ TEXT(4) code
45
+ TEXT() ctx
46
+ TEXT() hdr
47
+ }
48
+
35
49
  .. automodule:: ramses_rf.database
36
50
  :members:
37
51
  :show-inheritance:
@@ -24,14 +24,19 @@ RAMSES RF - Message database and index.
24
24
  from __future__ import annotations
25
25
 
26
26
  import asyncio
27
+ import contextlib
27
28
  import logging
29
+ import os
28
30
  import sqlite3
31
+ import uuid
29
32
  from collections import OrderedDict
30
33
  from datetime import datetime as dt, timedelta as td
31
34
  from typing import TYPE_CHECKING, Any, NewType
32
35
 
33
36
  from ramses_tx import CODES_SCHEMA, RQ, Code, Message, Packet
34
37
 
38
+ from .storage import StorageWorker
39
+
35
40
  if TYPE_CHECKING:
36
41
  DtmStrT = NewType("DtmStrT", str)
37
42
  MsgDdT = OrderedDict[DtmStrT, Message]
@@ -89,22 +94,55 @@ class MessageIndex:
89
94
 
90
95
  _housekeeping_task: asyncio.Task[None]
91
96
 
92
- def __init__(self, maintain: bool = True) -> None:
97
+ def __init__(self, maintain: bool = True, db_path: str = ":memory:") -> None:
93
98
  """Instantiate a message database/index."""
94
99
 
95
100
  self.maintain = maintain
96
101
  self._msgs: MsgDdT = OrderedDict() # stores all messages for retrieval.
97
102
  # Filled & cleaned up in housekeeping_loop.
98
103
 
99
- # Connect to a SQLite DB in memory
104
+ # For :memory: databases with multiple connections (Reader vs Worker)
105
+ # We must use a Shared Cache URI so both threads see the same data.
106
+ if db_path == ":memory:":
107
+ # Unique ID ensures parallel tests don't share the same in-memory DB
108
+ db_path = f"file:ramses_rf_{uuid.uuid4()}?mode=memory&cache=shared"
109
+
110
+ # Start the Storage Worker (Write Connection)
111
+ # This thread handles all blocking INSERT/UPDATE operations
112
+ self._worker = StorageWorker(db_path)
113
+
114
+ # Wait for the worker to create the tables.
115
+ # This prevents "no such table" errors on immediate reads.
116
+ if not self._worker.wait_for_ready(timeout=10.0):
117
+ _LOGGER.error("MessageIndex: StorageWorker timed out initializing database")
118
+
119
+ # Connect to a SQLite DB (Read Connection)
100
120
  self._cx = sqlite3.connect(
101
- ":memory:", detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES
121
+ db_path,
122
+ detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES,
123
+ check_same_thread=False,
124
+ uri=True, # Enable URI parsing for shared memory support
125
+ timeout=10.0, # Increased timeout to reduce 'database locked' errors
126
+ isolation_level=None, # Autocommit mode prevents stale snapshots
102
127
  )
128
+
129
+ # Enable Write-Ahead Logging for Reader as well
130
+ if db_path != ":memory:" and "mode=memory" not in db_path:
131
+ with contextlib.suppress(sqlite3.Error):
132
+ self._cx.execute("PRAGMA journal_mode=WAL")
133
+ elif "cache=shared" in db_path:
134
+ # Shared cache (used in tests) requires read_uncommitted to prevent
135
+ # readers from blocking writers (Table Locking).
136
+ with contextlib.suppress(sqlite3.Error):
137
+ self._cx.execute("PRAGMA read_uncommitted = true")
138
+
103
139
  # detect_types should retain dt type on store/retrieve
104
140
  self._cu = self._cx.cursor() # Create a cursor
105
141
 
106
142
  _setup_db_adapters() # DTM adapter/converter
107
- self._setup_db_schema()
143
+
144
+ # Schema creation is now handled safely by the StorageWorker to avoid races.
145
+ # self._setup_db_schema()
108
146
 
109
147
  if self.maintain:
110
148
  self._lock = asyncio.Lock()
@@ -137,6 +175,7 @@ class MessageIndex:
137
175
  ):
138
176
  self._housekeeping_task.cancel() # stop the housekeeper
139
177
 
178
+ self._worker.stop() # Stop the background thread
140
179
  self._cx.commit() # just in case
141
180
  self._cx.close() # may still need to do queries after engine has stopped?
142
181
 
@@ -145,6 +184,13 @@ class MessageIndex:
145
184
  """Return the messages in the index in a threadsafe way."""
146
185
  return self._msgs
147
186
 
187
+ def flush(self) -> None:
188
+ """Flush the storage worker queue.
189
+
190
+ This is primarily for testing to ensure data persistence before querying.
191
+ """
192
+ self._worker.flush()
193
+
148
194
  def _setup_db_schema(self) -> None:
149
195
  """Set up the message database schema.
150
196
 
@@ -236,23 +282,30 @@ class MessageIndex:
236
282
  dup: tuple[Message, ...] = tuple() # avoid UnboundLocalError
237
283
  old: Message | None = None # avoid UnboundLocalError
238
284
 
285
+ # Check in-memory cache for collision instead of blocking SQL
286
+ dtm_str: DtmStrT = msg.dtm.isoformat(timespec="microseconds") # type: ignore[assignment]
287
+ if dtm_str in self._msgs:
288
+ dup = (self._msgs[dtm_str],)
289
+
239
290
  try: # TODO: remove this, or apply only when source is a real packet log?
240
291
  # await self._lock.acquire()
241
- dup = self._delete_from( # HACK: because of contrived pkt logs
242
- dtm=msg.dtm # stored as such with DTM formatter
243
- )
244
- old = self._insert_into(msg) # will delete old msg by hdr (not dtm!)
292
+ # dup = self._delete_from( # HACK: because of contrived pkt logs
293
+ # dtm=msg.dtm # stored as such with DTM formatter
294
+ # )
295
+ # We defer the write to the worker; return value (old) is not available synchronously
296
+ self._insert_into(msg) # will delete old msg by hdr (not dtm!)
245
297
 
246
298
  except (
247
299
  sqlite3.Error
248
300
  ): # UNIQUE constraint failed: ? messages.dtm or .hdr (so: HACK)
249
- self._cx.rollback()
301
+ # self._cx.rollback()
302
+ pass
250
303
 
251
304
  else:
252
305
  # _msgs dict requires a timestamp reformat
253
- dtm: DtmStrT = msg.dtm.isoformat(timespec="microseconds") # type: ignore[assignment]
306
+ # dtm: DtmStrT = msg.dtm.isoformat(timespec="microseconds")
254
307
  # add msg to self._msgs dict
255
- self._msgs[dtm] = msg
308
+ self._msgs[dtm_str] = msg
256
309
 
257
310
  finally:
258
311
  pass # self._lock.release()
@@ -288,39 +341,36 @@ class MessageIndex:
288
341
  dtm: DtmStrT = _now.isoformat(timespec="microseconds") # type: ignore[assignment]
289
342
  hdr = f"{code}|{verb}|{src}|{payload}"
290
343
 
291
- dup = self._delete_from(hdr=hdr)
344
+ # dup = self._delete_from(hdr=hdr)
345
+ # Avoid blocking read; worker handles REPLACE on unique constraint collision
346
+
347
+ # Prepare data tuple for worker
348
+ data = (
349
+ _now,
350
+ verb,
351
+ src,
352
+ src,
353
+ code,
354
+ None,
355
+ hdr,
356
+ "|",
357
+ )
292
358
 
293
- sql = """
294
- INSERT INTO messages (dtm, verb, src, dst, code, ctx, hdr, plk)
295
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
296
- """
297
- try:
298
- self._cu.execute(
299
- sql,
300
- (
301
- _now,
302
- verb,
303
- src,
304
- src,
305
- code,
306
- None,
307
- hdr,
308
- "|",
309
- ),
310
- )
311
- except sqlite3.Error:
312
- self._cx.rollback()
313
- else:
314
- # also add dummy 3220 msg to self._msgs dict to allow maintenance loop
315
- msg: Message = Message._from_pkt(
316
- Packet(
317
- _now, f"... {verb} --- {src} --:------ {src} {code} 005 0000000000"
318
- )
319
- )
320
- self._msgs[dtm] = msg
359
+ self._worker.submit_packet(data)
360
+
361
+ # Backward compatibility for Tests:
362
+ # Check specific env var set by pytest, which is more reliable than sys.modules
363
+ if "PYTEST_CURRENT_TEST" in os.environ:
364
+ self.flush()
365
+
366
+ # also add dummy 3220 msg to self._msgs dict to allow maintenance loop
367
+ msg: Message = Message._from_pkt(
368
+ Packet(_now, f"... {verb} --- {src} --:------ {src} {code} 005 0000000000")
369
+ )
370
+ self._msgs[dtm] = msg
321
371
 
322
- if dup: # expected when more than one heat system in schema
323
- _LOGGER.debug("Replaced record with same hdr: %s", hdr)
372
+ # if dup: # expected when more than one heat system in schema
373
+ # _LOGGER.debug("Replaced record with same hdr: %s", hdr)
324
374
 
325
375
  def _insert_into(self, msg: Message) -> Message | None:
326
376
  """
@@ -337,29 +387,31 @@ class MessageIndex:
337
387
  else:
338
388
  msg_pkt_ctx = msg._pkt._ctx # can be None
339
389
 
340
- _old_msgs = self._delete_from(hdr=msg._pkt._hdr)
390
+ # _old_msgs = self._delete_from(hdr=msg._pkt._hdr)
391
+ # Refactor: Worker uses INSERT OR REPLACE to handle collision
392
+
393
+ data = (
394
+ msg.dtm,
395
+ str(msg.verb),
396
+ msg.src.id,
397
+ msg.dst.id,
398
+ str(msg.code),
399
+ msg_pkt_ctx,
400
+ msg._pkt._hdr,
401
+ payload_keys(msg.payload),
402
+ )
341
403
 
342
- sql = """
343
- INSERT INTO messages (dtm, verb, src, dst, code, ctx, hdr, plk)
344
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
345
- """
404
+ self._worker.submit_packet(data)
405
+
406
+ # Backward compatibility for Tests:
407
+ # Tests assume the DB update is instant. If running in pytest, flush immediately.
408
+ # This effectively makes the operation synchronous during tests to avoid rewriting tests.
409
+ if "PYTEST_CURRENT_TEST" in os.environ:
410
+ self.flush()
346
411
 
347
- self._cu.execute(
348
- sql,
349
- (
350
- msg.dtm,
351
- str(msg.verb),
352
- msg.src.id,
353
- msg.dst.id,
354
- str(msg.code),
355
- msg_pkt_ctx,
356
- msg._pkt._hdr,
357
- payload_keys(msg.payload),
358
- ),
359
- )
360
412
  # _LOGGER.debug(f"Added {msg} to gwy.msg_db")
361
413
 
362
- return _old_msgs[0] if _old_msgs else None
414
+ return None
363
415
 
364
416
  def rem(
365
417
  self, msg: Message | None = None, **kwargs: str | dt
@@ -0,0 +1,168 @@
1
+ """RAMSES RF - Background storage worker for async I/O."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import contextlib
6
+ import logging
7
+ import queue
8
+ import sqlite3
9
+ import threading
10
+ from typing import Any
11
+
12
+ _LOGGER = logging.getLogger(__name__)
13
+
14
+
15
+ class StorageWorker:
16
+ """A background worker thread to handle blocking storage I/O asynchronously."""
17
+
18
+ def __init__(self, db_path: str = ":memory:"):
19
+ """Initialize the storage worker thread."""
20
+ self._db_path = db_path
21
+ self._queue: queue.SimpleQueue[tuple[str, Any] | None] = queue.SimpleQueue()
22
+ self._ready_event = threading.Event()
23
+
24
+ self._thread = threading.Thread(
25
+ target=self._run,
26
+ name="RamsesStorage",
27
+ daemon=True, # FIX: Set to True so the process can exit even if stop() is missed
28
+ )
29
+ self._thread.start()
30
+
31
+ def wait_for_ready(self, timeout: float | None = None) -> bool:
32
+ """Wait until the database is initialized and ready."""
33
+ return self._ready_event.wait(timeout)
34
+
35
+ def submit_packet(self, packet_data: tuple[Any, ...]) -> None:
36
+ """Submit a packet tuple for SQL insertion (Non-blocking)."""
37
+ self._queue.put(("SQL", packet_data))
38
+
39
+ def flush(self, timeout: float = 10.0) -> None:
40
+ """Block until all currently pending tasks are processed."""
41
+ # REMOVED: if self._queue.empty(): return
42
+ # This check caused a race condition where flush() returned before
43
+ # the worker finished committing the last item it just popped.
44
+
45
+ # We inject a special marker into the queue
46
+ sentinel = threading.Event()
47
+ self._queue.put(("MARKER", sentinel))
48
+
49
+ # Wait for the worker to set the sentinel
50
+ if not sentinel.wait(timeout):
51
+ _LOGGER.warning("StorageWorker flush timed out")
52
+
53
+ def stop(self) -> None:
54
+ """Signal the worker to stop processing and close resources."""
55
+ self._queue.put(None) # Poison pill
56
+ self._thread.join()
57
+
58
+ def _init_db(self, conn: sqlite3.Connection) -> None:
59
+ """Initialize the database schema."""
60
+ cursor = conn.cursor()
61
+ cursor.execute(
62
+ """
63
+ CREATE TABLE IF NOT EXISTS messages (
64
+ dtm DTM NOT NULL PRIMARY KEY,
65
+ verb TEXT(2) NOT NULL,
66
+ src TEXT(12) NOT NULL,
67
+ dst TEXT(12) NOT NULL,
68
+ code TEXT(4) NOT NULL,
69
+ ctx TEXT,
70
+ hdr TEXT NOT NULL UNIQUE,
71
+ plk TEXT NOT NULL
72
+ )
73
+ """
74
+ )
75
+ # Create indexes to speed up future reads
76
+ for col in ("verb", "src", "dst", "code", "ctx", "hdr"):
77
+ cursor.execute(f"CREATE INDEX IF NOT EXISTS idx_{col} ON messages ({col})")
78
+ conn.commit()
79
+
80
+ def _run(self) -> None:
81
+ """The main loop running in the background thread."""
82
+ _LOGGER.debug("StorageWorker thread started.")
83
+
84
+ # Setup SQLite connection in this thread
85
+ try:
86
+ # uri=True allows opening "file::memory:?cache=shared"
87
+ conn = sqlite3.connect(
88
+ self._db_path,
89
+ detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES,
90
+ check_same_thread=False,
91
+ uri=True,
92
+ timeout=10.0, # Increased timeout for locking
93
+ )
94
+
95
+ # Enable Write-Ahead Logging for concurrency
96
+ if self._db_path != ":memory:" and "mode=memory" not in self._db_path:
97
+ with contextlib.suppress(sqlite3.Error):
98
+ conn.execute("PRAGMA journal_mode=WAL")
99
+ conn.execute("PRAGMA synchronous=NORMAL")
100
+ elif "cache=shared" in self._db_path:
101
+ with contextlib.suppress(sqlite3.Error):
102
+ conn.execute("PRAGMA read_uncommitted = true")
103
+
104
+ self._init_db(conn)
105
+ self._ready_event.set() # Signal that tables exist
106
+ except sqlite3.Error as exc:
107
+ _LOGGER.error("Failed to initialize storage database: %s", exc)
108
+ self._ready_event.set() # Avoid blocking waiters forever
109
+ return
110
+
111
+ while True:
112
+ try:
113
+ # Block here waiting for work
114
+ item = self._queue.get()
115
+
116
+ if item is None: # Shutdown signal
117
+ break
118
+
119
+ task_type, data = item
120
+
121
+ if task_type == "MARKER":
122
+ # Flush requested
123
+ data.set()
124
+ continue
125
+
126
+ if task_type == "SQL":
127
+ # Optimization: Batch processing
128
+ batch = [data]
129
+ # Drain queue of pending SQL tasks to bulk insert
130
+ while not self._queue.empty():
131
+ try:
132
+ # Peek/get next item without blocking
133
+ next_item = self._queue.get_nowait()
134
+ if next_item is None:
135
+ self._queue.put(None) # Re-queue poison pill
136
+ break
137
+
138
+ next_type, next_data = next_item
139
+ if next_type == "SQL":
140
+ batch.append(next_data)
141
+ elif next_type == "MARKER":
142
+ # Handle marker after this batch
143
+ self._queue.put(next_item) # Re-queue marker
144
+ break
145
+ else:
146
+ pass
147
+ except queue.Empty:
148
+ break
149
+
150
+ try:
151
+ conn.executemany(
152
+ """
153
+ INSERT OR REPLACE INTO messages
154
+ (dtm, verb, src, dst, code, ctx, hdr, plk)
155
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
156
+ """,
157
+ batch,
158
+ )
159
+ conn.commit()
160
+ except sqlite3.Error as err:
161
+ _LOGGER.error("SQL Write Failed: %s", err)
162
+
163
+ except Exception as err:
164
+ _LOGGER.exception("StorageWorker encountered an error: %s", err)
165
+
166
+ # Cleanup
167
+ conn.close()
168
+ _LOGGER.debug("StorageWorker thread stopped.")
@@ -1,4 +1,4 @@
1
1
  """RAMSES RF - a RAMSES-II protocol decoder & analyser (application layer)."""
2
2
 
3
- __version__ = "0.53.1"
3
+ __version__ = "0.53.3"
4
4
  VERSION = __version__
@@ -3109,7 +3109,9 @@ def parser_31da(payload: str, msg: Message) -> PayDictT._31DA:
3109
3109
  if len(payload) == 58:
3110
3110
  return result # type: ignore[return-value]
3111
3111
 
3112
- result.update({"_extra": payload[58:]}) # sporadic [58:60] always 00
3112
+ result.update(
3113
+ {"_extra": payload[58:]}
3114
+ ) # sporadic [58:60] one of {00, 20, 40} version?
3113
3115
  return result # type: ignore[return-value]
3114
3116
 
3115
3117
  # From an Orcon 15RF Display
@@ -551,7 +551,7 @@ CODES_SCHEMA: dict[Code, dict[str, Any]] = { # rf_unknown
551
551
  },
552
552
  Code._31DA: { # hvac_state (fan_state_extended)
553
553
  SZ_NAME: "hvac_state",
554
- I_: r"^(00|01|15|16|17|21)[0-9A-F]{56}(00|20)?$",
554
+ I_: r"^(00|01|15|16|17|21)[0-9A-F]{56}(00|20|40)?$",
555
555
  RQ: r"^(00|01|15|16|17|21)$",
556
556
  # RQ --- 32:168090 30:082155 --:------ 31DA 001 21
557
557
  },
@@ -49,6 +49,7 @@ import logging
49
49
  import os
50
50
  import re
51
51
  import sys
52
+ import time
52
53
  from collections import deque
53
54
  from collections.abc import Awaitable, Callable, Iterable
54
55
  from datetime import datetime as dt, timedelta as td
@@ -988,7 +989,8 @@ class FileTransport(_ReadTransport, _FileTransportAbstractor):
988
989
  """Start the reader task."""
989
990
  self._reading = True
990
991
  try:
991
- await self._reader()
992
+ # await self._reader()
993
+ await self.loop.run_in_executor(None, self._blocking_reader)
992
994
  except Exception as err:
993
995
  self.loop.call_soon_threadsafe(
994
996
  functools.partial(self._protocol.connection_lost, err) # type: ignore[arg-type]
@@ -998,50 +1000,48 @@ class FileTransport(_ReadTransport, _FileTransportAbstractor):
998
1000
  functools.partial(self._protocol.connection_lost, None)
999
1001
  )
1000
1002
 
1001
- # NOTE: self._frame_read() invoked from here
1002
- async def _reader(self) -> None: # TODO
1003
+ def _blocking_reader(self) -> None:
1003
1004
  """Loop through the packet source for Frames and process them."""
1004
1005
 
1005
1006
  if isinstance(self._pkt_source, dict):
1006
1007
  for dtm_str, pkt_line in self._pkt_source.items(): # assume dtm_str is OK
1007
- while not self._reading:
1008
- await asyncio.sleep(0.001)
1009
- self._frame_read(dtm_str, pkt_line)
1010
- await asyncio.sleep(0)
1011
- # NOTE: instable without, big performance penalty if delay >0
1008
+ self._process_line(dtm_str, pkt_line)
1012
1009
 
1013
1010
  elif isinstance(self._pkt_source, str): # file_name, used in client parse
1014
1011
  # open file file_name before reading
1015
1012
  try:
1016
1013
  with fileinput.input(files=self._pkt_source, encoding="utf-8") as file:
1017
1014
  for dtm_pkt_line in file: # self._pkt_source:
1018
- # TODO check dtm_str is OK
1019
- while not self._reading:
1020
- await asyncio.sleep(0.001)
1021
- # there may be blank lines in annotated log files
1022
- if (dtm_pkt_line := dtm_pkt_line.strip()) and dtm_pkt_line[
1023
- :1
1024
- ] != "#":
1025
- self._frame_read(dtm_pkt_line[:26], dtm_pkt_line[27:])
1026
- # this is where the parsing magic happens!
1027
- await asyncio.sleep(0)
1028
- # NOTE: instable without, big performance penalty if delay >0
1015
+ self._process_line_from_raw(dtm_pkt_line)
1029
1016
  except FileNotFoundError as err:
1030
1017
  _LOGGER.warning(f"Correct the packet file name; {err}")
1018
+
1031
1019
  elif isinstance(self._pkt_source, TextIOWrapper): # used by client monitor
1032
1020
  for dtm_pkt_line in self._pkt_source: # should check dtm_str is OK
1033
- while not self._reading:
1034
- await asyncio.sleep(0.001)
1035
- # can be blank lines in annotated log files
1036
- if (dtm_pkt_line := dtm_pkt_line.strip()) and dtm_pkt_line[:1] != "#":
1037
- self._frame_read(dtm_pkt_line[:26], dtm_pkt_line[27:])
1038
- await asyncio.sleep(0)
1039
- # NOTE: instable without, big performance penalty if delay >0
1021
+ self._process_line_from_raw(dtm_pkt_line)
1022
+
1040
1023
  else:
1041
1024
  raise exc.TransportSourceInvalid(
1042
1025
  f"Packet source is not dict, TextIOWrapper or str: {self._pkt_source:!r}"
1043
1026
  )
1044
1027
 
1028
+ def _process_line_from_raw(self, line: str) -> None:
1029
+ """Helper to process raw lines."""
1030
+ # there may be blank lines in annotated log files
1031
+ if (line := line.strip()) and line[:1] != "#":
1032
+ self._process_line(line[:26], line[27:])
1033
+ # this is where the parsing magic happens!
1034
+
1035
+ def _process_line(self, dtm_str: str, frame: str) -> None:
1036
+ """Push frame to protocol in a thread-safe way."""
1037
+ while not self._reading:
1038
+ time.sleep(0.001)
1039
+
1040
+ self._frame_read(dtm_str, frame)
1041
+
1042
+ # NOTE: instable without, big performance penalty if delay >0
1043
+ time.sleep(0)
1044
+
1045
1045
  def _close(self, exc: exc.RamsesException | None = None) -> None:
1046
1046
  """Close the transport (cancel any outstanding tasks).
1047
1047