boneIO 0.11.0.dev5__tar.gz → 0.11.0.dev8__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 (274) hide show
  1. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/PKG-INFO +1 -1
  2. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/const.py +2 -0
  3. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/config.py +18 -1
  4. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/filter.py +3 -2
  5. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/ha_discovery.py +2 -2
  6. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/loader.py +31 -4
  7. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/util.py +26 -1
  8. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/yaml_util.py +72 -1
  9. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/manager.py +80 -53
  10. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/message_bus/mqtt.py +3 -3
  11. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/client.py +68 -65
  12. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/liquid-sensor.json +11 -0
  13. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/sensor.py +82 -3
  14. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/single_sensor.py +50 -0
  15. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/oled.py +8 -8
  16. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/relay/basic.py +164 -95
  17. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/runner.py +17 -15
  18. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/actions.yaml +13 -11
  19. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/schema.yaml +35 -2
  20. boneio-0.11.0.dev8/boneio/version.py +2 -0
  21. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/binary_sensor.schema.json +12 -8
  22. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/config.schema.json +69 -21
  23. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/event.schema.json +18 -12
  24. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/modbus_sensors.schema.json +30 -0
  25. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/mqtt.schema.json +0 -1
  26. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/output.schema.json +9 -0
  27. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/pyproject.toml +2 -2
  28. boneio-0.11.0.dev5/boneio/version.py +0 -2
  29. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/LICENSE +0 -0
  30. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/README.md +0 -0
  31. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/__init__.py +0 -0
  32. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.7/input.yaml +0 -0
  33. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.7/output_24_16.yaml +0 -0
  34. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.7/output_32_10.yaml +0 -0
  35. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.7/output_cover.yaml +0 -0
  36. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.7/output_cover_mix.yaml +0 -0
  37. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.8/input.yaml +0 -0
  38. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.8/output_24_16.yaml +0 -0
  39. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.8/output_32_10.yaml +0 -0
  40. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.8/output_cover.yaml +0 -0
  41. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/boards/0.8/output_cover_mix.yaml +0 -0
  42. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/bonecli.py +0 -0
  43. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/cover/__init__.py +0 -0
  44. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/cover/cover.py +0 -0
  45. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/cover/previous.py +0 -0
  46. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/cover/time_based.py +0 -0
  47. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/cover/venetian.py +0 -0
  48. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/24x16/adc.yaml +0 -0
  49. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/24x16/binary_sensor.yaml +0 -0
  50. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/24x16/config.yaml +0 -0
  51. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/24x16/event.yaml +0 -0
  52. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/24x16/mqtt.yaml +0 -0
  53. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/24x16/output24x16A.yaml +0 -0
  54. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/32x10/adc.yaml +0 -0
  55. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/32x10/binary_sensor.yaml +0 -0
  56. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/32x10/config.yaml +0 -0
  57. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/32x10/event.yaml +0 -0
  58. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/32x10/mqtt.yaml +0 -0
  59. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/32x10/output32x10A.yaml +0 -0
  60. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/__init__.py +0 -0
  61. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover/adc.yaml +0 -0
  62. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover/binary_sensor.yaml +0 -0
  63. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover/config.yaml +0 -0
  64. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover/cover.yaml +0 -0
  65. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover/event.yaml +0 -0
  66. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover/mqtt.yaml +0 -0
  67. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover/outputCover.yaml +0 -0
  68. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover_mix/adc.yaml +0 -0
  69. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover_mix/binary_sensor.yaml +0 -0
  70. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover_mix/config.yaml +0 -0
  71. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover_mix/cover.yaml +0 -0
  72. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover_mix/event.yaml +0 -0
  73. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover_mix/mqtt.yaml +0 -0
  74. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/cover_mix/outputCoverMix.yaml +0 -0
  75. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/adc.yaml +0 -0
  76. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/all_binary_sensor.yaml +0 -0
  77. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/binary_sensor.yaml +0 -0
  78. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/binary_sensor_v_0_7.yaml +0 -0
  79. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/config_24_16.yaml +0 -0
  80. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/event_all.yaml +0 -0
  81. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/event_v_0_7.yaml +0 -0
  82. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/led32x4A.yaml +0 -0
  83. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/output24x16A.yaml +0 -0
  84. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/output24x16A_v0.3.yaml +0 -0
  85. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/output32x10A.yaml +0 -0
  86. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/example_config/different_configs/output32x5A.yaml +0 -0
  87. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/fonts/danube__.ttf +0 -0
  88. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/group/__init__.py +0 -0
  89. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/group/output.py +0 -0
  90. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/__init__.py +0 -0
  91. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/async_updater.py +0 -0
  92. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/click_timer.py +0 -0
  93. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/events.py +0 -0
  94. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/exceptions.py +0 -0
  95. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/gpio.py +0 -0
  96. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/gpiod.py +0 -0
  97. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/interlock.py +0 -0
  98. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/logger.py +0 -0
  99. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/mqtt.py +0 -0
  100. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/oled.py +0 -0
  101. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/onewire/W1ThermSensor.py +0 -0
  102. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/onewire/__init__.py +0 -0
  103. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/onewire/ds2482.py +0 -0
  104. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/onewire/onewire.py +0 -0
  105. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/pcf8575.py +0 -0
  106. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/queue.py +0 -0
  107. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/schema_converter.py +0 -0
  108. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/sensor/ina_219_smbus.py +0 -0
  109. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/state_manager.py +0 -0
  110. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/stats.py +0 -0
  111. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/helper/timeperiod.py +0 -0
  112. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/input/__init__.py +0 -0
  113. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/input/gpio.py +0 -0
  114. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/input/gpio_new.py +0 -0
  115. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/message_bus/__init__.py +0 -0
  116. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/message_bus/basic.py +0 -0
  117. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/message_bus/local.py +0 -0
  118. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/__init__.py +0 -0
  119. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/cwt.json +0 -0
  120. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/dts1964_3f.json +0 -0
  121. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/modbuscli.py +0 -0
  122. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/orno-or-we-517.json +0 -0
  123. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/pt100.json +0 -0
  124. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/r4dcb08.json +0 -0
  125. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/sdm120.json +0 -0
  126. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/sdm630.json +0 -0
  127. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/sht20.json +0 -0
  128. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/sht30.json +0 -0
  129. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/socomec_e03.json +0 -0
  130. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/socomec_e23.json +0 -0
  131. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/sofar.json +0 -0
  132. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/modbus/utils.py +0 -0
  133. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/models/__init__.py +0 -0
  134. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/models/files.py +0 -0
  135. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/models/logs.py +0 -0
  136. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/relay/__init__.py +0 -0
  137. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/relay/gpio.py +0 -0
  138. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/relay/mcp.py +0 -0
  139. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/relay/pca.py +0 -0
  140. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/relay/pcf.py +0 -0
  141. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/actions_sensor.yaml +0 -0
  142. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/actions_switch.yaml +0 -0
  143. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/filters.yaml +0 -0
  144. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/filters_adc.yaml +0 -0
  145. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/id.yaml +0 -0
  146. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/temp_unit.yaml +0 -0
  147. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/schema/update_interval.yaml +0 -0
  148. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/__init__.py +0 -0
  149. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/adc.py +0 -0
  150. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/gpio.py +0 -0
  151. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/gpio_new.py +0 -0
  152. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/ina219.py +0 -0
  153. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/serial_number.py +0 -0
  154. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/temp/__init__.py +0 -0
  155. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/temp/dallas.py +0 -0
  156. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/temp/lm75.py +0 -0
  157. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/sensor/temp/mcp9808.py +0 -0
  158. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/app.py +0 -0
  159. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/abap-BrgZPUOV.js +0 -0
  160. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/apex-DyP6w7ZV.js +0 -0
  161. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/azcli-BaLxmfj-.js +0 -0
  162. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/bat-CFOPXBzS.js +0 -0
  163. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/bicep-BfEKNvv3.js +0 -0
  164. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/cameligo-BFG1Mk7z.js +0 -0
  165. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/clojure-DTECt2xU.js +0 -0
  166. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/codicon-DCmgc-ay.ttf +0 -0
  167. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/coffee-CDGzqUPQ.js +0 -0
  168. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/cpp-CLLBncYj.js +0 -0
  169. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/csharp-dUCx_-0o.js +0 -0
  170. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/csp-5Rap-vPy.js +0 -0
  171. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/css-D3h14YRZ.js +0 -0
  172. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/cssMode-C5afUc0K.js +0 -0
  173. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/cypher-DrQuvNYM.js +0 -0
  174. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/dart-CFKIUWau.js +0 -0
  175. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/dockerfile-Zznr-cwX.js +0 -0
  176. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/ecl-Ce3n6wWz.js +0 -0
  177. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/editor.worker-oRlJJsnX.js +0 -0
  178. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/elixir-deUWdS0T.js +0 -0
  179. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/flow9-i9-g7ZhI.js +0 -0
  180. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/freemarker2-emRcpycN.js +0 -0
  181. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/fsharp-CzKuDChf.js +0 -0
  182. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/go-Cphgjts3.js +0 -0
  183. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/graphql-Cg7bfA9N.js +0 -0
  184. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/handlebars-mMSD-c9g.js +0 -0
  185. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/hcl-0cvrggvQ.js +0 -0
  186. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/html-Clq5L_-r.js +0 -0
  187. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/htmlMode-CYqwdxDX.js +0 -0
  188. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/index-BcJKL9Rz.js +0 -0
  189. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/index-DzcnWIxb.css +0 -0
  190. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/ini-Drc7WvVn.js +0 -0
  191. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/java-B_fMsGYe.js +0 -0
  192. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/javascript-BR7Nbw1G.js +0 -0
  193. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/json.worker-BnUULff4.js +0 -0
  194. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/jsonMode-aJvzz7xf.js +0 -0
  195. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/julia-Bqgm2twL.js +0 -0
  196. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/kotlin-BSkB5QuD.js +0 -0
  197. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/less-BsTHnhdd.js +0 -0
  198. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/lexon-YWi4-JPR.js +0 -0
  199. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/liquid-BNyvmcbu.js +0 -0
  200. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/lua-nf6ki56Z.js +0 -0
  201. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/m3-Cpb6xl2v.js +0 -0
  202. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/markdown-DSZPf7rp.js +0 -0
  203. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/mdx-pWhQfMuK.js +0 -0
  204. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/mips-B_c3zf-v.js +0 -0
  205. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/msdax-rUNN04Wq.js +0 -0
  206. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/mysql-DDwshQtU.js +0 -0
  207. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/objective-c-B5zXfXm9.js +0 -0
  208. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/pascal-CXOwvkN_.js +0 -0
  209. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/pascaligo-Bc-ZgV77.js +0 -0
  210. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/perl-CwNk8-XU.js +0 -0
  211. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/pgsql-tGk8EFnU.js +0 -0
  212. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/php-CpIb_Oan.js +0 -0
  213. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/pla-B03wrqEc.js +0 -0
  214. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/postiats-BKlk5iyT.js +0 -0
  215. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/powerquery-Bhzvs7bI.js +0 -0
  216. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/powershell-Dd3NCNK9.js +0 -0
  217. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/protobuf-COyEY5Pt.js +0 -0
  218. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/pug-BaJupSGV.js +0 -0
  219. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/python-CVtAQrrC.js +0 -0
  220. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/qsharp-DXyYeYxl.js +0 -0
  221. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/r-CdQndTaG.js +0 -0
  222. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/razor-BBiV-C6e.js +0 -0
  223. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/redis-CVwtpugi.js +0 -0
  224. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/redshift-25W9uPmb.js +0 -0
  225. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/restructuredtext-DfzH4Xui.js +0 -0
  226. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/ruby-Cp1zYvxS.js +0 -0
  227. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/rust-D5C2fndG.js +0 -0
  228. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/sb-CDntyWJ8.js +0 -0
  229. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/scala-BoFRg7Ot.js +0 -0
  230. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/scheme-Bio4gycK.js +0 -0
  231. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/scss-4Ik7cdeQ.js +0 -0
  232. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/shell-CX-rkNHf.js +0 -0
  233. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/solidity-Tw7wswEv.js +0 -0
  234. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/sophia-C5WLch3f.js +0 -0
  235. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/sparql-DHaeiCBh.js +0 -0
  236. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/sql-CCSDG5nI.js +0 -0
  237. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/st-pnP8ivHi.js +0 -0
  238. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/swift-DwJ7jVG9.js +0 -0
  239. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/systemverilog-B9Xyijhd.js +0 -0
  240. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/tcl-DnHyzjbg.js +0 -0
  241. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/tsMode-COZIvtMx.js +0 -0
  242. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/twig-CPajHgWi.js +0 -0
  243. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/typescript-DKdGc2VX.js +0 -0
  244. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/typespec-D-MeaMDU.js +0 -0
  245. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/vb-DgyLZaXg.js +0 -0
  246. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/wgsl-BIv9DU6q.js +0 -0
  247. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/xml-CTK9ybeu.js +0 -0
  248. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/yaml-ggTgJ1wa.js +0 -0
  249. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/assets/yaml.worker-DSHdx_mx.js +0 -0
  250. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/boneio.svg +0 -0
  251. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/boneio_fav.svg +0 -0
  252. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/frontend-dist/index.html +0 -0
  253. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/adc.schema.json +0 -0
  254. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/boneio.schema.json +0 -0
  255. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/cover.schema.json +0 -0
  256. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/dallas.schema.json +0 -0
  257. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/ds2482.schema.json +0 -0
  258. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/ina219.schema.json +0 -0
  259. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/lm75.schema.json +0 -0
  260. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/logger.schema.json +0 -0
  261. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/mcp23017.schema.json +0 -0
  262. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/mcp9808.schema.json +0 -0
  263. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/modbus.schema.json +0 -0
  264. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/oled.schema.json +0 -0
  265. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/output_group.schema.json +0 -0
  266. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/pca9685.schema.json +0 -0
  267. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/pcf8575.schema.json +0 -0
  268. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/sensor.schema.json +0 -0
  269. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/schema/web.schema.json +0 -0
  270. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/web_server.py +0 -0
  271. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/boneio/webui/websocket_manager.py +0 -0
  272. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/tests/32_5_config.yaml +0 -0
  273. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/tests/bandit.yaml +0 -0
  274. {boneio-0.11.0.dev5 → boneio-0.11.0.dev8}/tests/relay_32_5.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: boneio
3
- Version: 0.11.0.dev5
3
+ Version: 0.11.0.dev8
4
4
  Summary: Python App for BoneIO
5
5
  License: GNU General Public License v3.0
6
6
  Author-email: Paweł Szafer <pszafer@gmail.com>
@@ -32,6 +32,8 @@ PCA = "pca"
32
32
  GPIO_MODE = "gpio_mode"
33
33
  ACTIONS = "actions"
34
34
  ACTION = "action"
35
+ OUTPUT_OVER_MQTT = "output_over_mqtt"
36
+ COVER_OVER_MQTT = "cover_over_mqtt"
35
37
  SWITCH = "switch"
36
38
  LIGHT = "light"
37
39
  BUTTON = "button"
@@ -3,6 +3,7 @@ Module to provide basic config options.
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
+ import logging
6
7
  from _collections_abc import dict_values
7
8
  from typing import Union
8
9
 
@@ -18,6 +19,9 @@ from boneio.const import (
18
19
  SWITCH,
19
20
  VALVE,
20
21
  )
22
+ from boneio.helper.util import sanitize_mqtt_topic
23
+
24
+ _LOGGER = logging.getLogger(__name__)
21
25
 
22
26
 
23
27
  class ConfigHelper:
@@ -28,9 +32,12 @@ class ConfigHelper:
28
32
  device_type: str = "boneIO Black",
29
33
  ha_discovery: bool = True,
30
34
  ha_discovery_prefix: str = HOMEASSISTANT,
35
+ network_info: dict = None,
36
+ is_web_active: bool = False,
31
37
  ):
32
38
  self._name = name
33
- self._topic_prefix = topic_prefix if topic_prefix else name
39
+ sanitized_topic_prefix = sanitize_mqtt_topic(topic_prefix) if topic_prefix else sanitize_mqtt_topic(name)
40
+ self._topic_prefix = sanitized_topic_prefix
34
41
  self._ha_discovery = ha_discovery
35
42
  self._ha_discovery_prefix = ha_discovery_prefix
36
43
  self._device_type = device_type
@@ -46,6 +53,16 @@ class ConfigHelper:
46
53
  VALVE: {},
47
54
  }
48
55
  self.manager_ready: bool = False
56
+ self._network_info = network_info
57
+ self._is_web_active = is_web_active
58
+
59
+ @property
60
+ def network_info(self) -> dict:
61
+ return self._network_info
62
+
63
+ @property
64
+ def is_web_active(self) -> bool:
65
+ return self._is_web_active
49
66
 
50
67
  @property
51
68
  def topic_prefix(self) -> str:
@@ -1,8 +1,9 @@
1
1
  """Filter class to adjust sensor values."""
2
2
 
3
3
  from __future__ import annotations
4
- from typing import Optional
4
+
5
5
  import logging
6
+ from typing import Optional
6
7
 
7
8
  _LOGGER = logging.getLogger(__name__)
8
9
 
@@ -13,7 +14,7 @@ FILTERS = {
13
14
  "multiply": lambda x, y: x * y if x else x,
14
15
  "filter_out": lambda x, y: None if x == y else x,
15
16
  "filter_out_greater": lambda x, y: None if x > y else x,
16
- "filter_out_lower": lambda x, y: None if x < y else x,
17
+ "filter_out_lower": lambda x, y: None if x < y else x
17
18
  }
18
19
 
19
20
 
@@ -228,12 +228,12 @@ def modbus_sensor_availabilty_message(
228
228
  ):
229
229
  """Create Modbus Sensor availability topic for HA."""
230
230
  return {
231
- "availability": [{"topic": f"{topic}/{id}{STATE}"}],
231
+ "availability": [{"topic": f"{topic}/{id}/{STATE}"}],
232
232
  "device": {
233
233
  "identifiers": [id],
234
234
  "manufacturer": "boneIO",
235
235
  "model": model,
236
- "name": f"boneIO {name.upper()}",
236
+ "name": name,
237
237
  "sw_version": __version__,
238
238
  },
239
239
  "name": sensor_id,
@@ -235,6 +235,7 @@ def create_modbus_sensors(manager: Manager, message_bus: MessageBus, sensors, **
235
235
  for sensor in sensors:
236
236
  name = sensor.get(ID)
237
237
  id = name.replace(" ", "")
238
+ additional_data = sensor.get("data", {})
238
239
  try:
239
240
  modbus_sensors[id.lower()] = ModbusSensor(
240
241
  address=sensor[ADDRESS],
@@ -247,6 +248,7 @@ def create_modbus_sensors(manager: Manager, message_bus: MessageBus, sensors, **
247
248
  UPDATE_INTERVAL, TimePeriod(seconds=60)
248
249
  ),
249
250
  sensors_filters=sensor.get("sensors_filters", {}),
251
+ additional_data=additional_data,
250
252
  **kwargs,
251
253
  )
252
254
  except FileNotFoundError as err:
@@ -361,8 +363,8 @@ def configure_relay(
361
363
  "output_type": output_type,
362
364
  }
363
365
  elif output_kind == GPIO:
364
- if GPIO not in manager.grouped_outputs:
365
- manager.grouped_outputs[GPIO] = {}
366
+ if GPIO not in manager.grouped_outputs_by_expander:
367
+ manager.grouped_outputs_by_expander[GPIO] = {}
366
368
  extra_args = {
367
369
  "pin": config.pop(PIN),
368
370
  }
@@ -389,8 +391,8 @@ def configure_relay(
389
391
  **extra_args,
390
392
  )
391
393
  manager._interlock_manager.register(relay, interlock_groups)
392
- manager.grouped_outputs[expander_id][relay_id] = relay
393
- if relay.virtual_power_usage is not None:
394
+ manager.grouped_outputs_by_expander[expander_id][relay_id] = relay
395
+ if relay.is_virtual_power:
394
396
  manager.send_ha_autodiscovery(
395
397
  id=f"{relay_id}_virtual_power",
396
398
  relay_id=relay_id,
@@ -415,6 +417,31 @@ def configure_relay(
415
417
  state_class="total_increasing",
416
418
  value_template="{{ value_json.energy }}"
417
419
  )
420
+ if relay.is_virtual_volume_flow_rate:
421
+ manager.send_ha_autodiscovery(
422
+ id=f"{relay_id}_virtual_volume_flow_rate",
423
+ relay_id=relay_id,
424
+ name=f"{name} Virtual Volume Flow Rate",
425
+ ha_type="sensor",
426
+ device_type="energy",
427
+ availability_msg_func=ha_virtual_energy_sensor_discovery_message,
428
+ unit_of_measurement="L/h",
429
+ device_class="volume_flow_rate",
430
+ state_class="measurement",
431
+ value_template="{{ value_json.volume_flow_rate }}"
432
+ )
433
+ manager.send_ha_autodiscovery(
434
+ id=f"{relay_id}_virtual_consumption",
435
+ relay_id=relay_id,
436
+ name=f"{name} Virtual consumption",
437
+ ha_type="sensor",
438
+ device_type="energy",
439
+ availability_msg_func=ha_virtual_energy_sensor_discovery_message,
440
+ unit_of_measurement="L",
441
+ device_class="water",
442
+ state_class="total_increasing",
443
+ value_template="{{ value_json.water }}"
444
+ )
418
445
  return relay
419
446
 
420
447
 
@@ -19,7 +19,7 @@ def is_callback(func: Callable[..., Any]) -> bool:
19
19
 
20
20
 
21
21
  def strip_accents(s):
22
-
22
+ """Remove accents and spaces from a string."""
23
23
  return "".join(
24
24
  c
25
25
  for c in unicodedata.normalize("NFD", s)
@@ -27,6 +27,31 @@ def strip_accents(s):
27
27
  )
28
28
 
29
29
 
30
+ def sanitize_mqtt_topic(name: str) -> str:
31
+ """
32
+ Sanitize a string to be used as an MQTT topic:
33
+ - Replace spaces with underscores
34
+ - Remove Polish diacritics
35
+ - Remove/replace forbidden characters (leave only a-z, A-Z, 0-9, '_', '-')
36
+ Args:
37
+ name (str): Input string
38
+ Returns:
39
+ str: Sanitized string
40
+ """
41
+ import re
42
+ from .logger import _LOGGER
43
+
44
+ original = name
45
+ # Zamień spacje na podkreślenia
46
+ name = name.replace(' ', '_')
47
+ # Usuń polskie znaki
48
+ name = strip_accents(name)
49
+ # Zostaw tylko dozwolone znaki
50
+ name = re.sub(r'[^a-zA-Z0-9_-]', '', name)
51
+ _LOGGER.debug(f"Sanitized MQTT topic: '{original}' -> '{name}'")
52
+ return name
53
+
54
+
30
55
  def open_json(path: str, model: str) -> dict:
31
56
  """Open json file."""
32
57
  file = f"{os.path.join(path)}/{model}.json"
@@ -430,6 +430,40 @@ class CustomValidator(Validator):
430
430
  def _normalize_coerce_actions_output(self, value):
431
431
  return str(value).upper()
432
432
 
433
+ def _normalize_coerce_length_to_meters(self, value) -> float:
434
+ """
435
+ Convert a length value to meters.
436
+ Accepts:
437
+ - Numeric values (int, float) - assumed to be in meters
438
+ - Strings with units: 'm', 'cm', 'mm'
439
+ Examples:
440
+ 5 -> 5.0
441
+ '5m' -> 5.0
442
+ '500cm' -> 5.0
443
+ '2000mm' -> 2.0
444
+ """
445
+ if value is None:
446
+ return None
447
+ if isinstance(value, (int, float)):
448
+ return float(value)
449
+ value = str(value).strip().lower()
450
+ match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(m|cm|mm)?$", value)
451
+ if not match:
452
+ raise ValueError(f"Invalid length value: {value}")
453
+ num = float(match.group(1))
454
+ unit = match.group(2) or "m"
455
+ if unit == "m":
456
+ multiplier = 1.0
457
+ elif unit == "cm":
458
+ multiplier = 0.01
459
+ elif unit == "mm":
460
+ multiplier = 0.001
461
+ else:
462
+ raise ValueError(f"Unknown unit for length value: {unit}")
463
+ result = num * multiplier
464
+ _LOGGER.debug(f"Parsed length value '{value}' as {result} m")
465
+ return result
466
+
433
467
  def _normalize_coerce_positive_time_period(self, value) -> TimePeriod:
434
468
  """Validate and transform time period with time unit and integer value."""
435
469
  if isinstance(value, int):
@@ -529,7 +563,6 @@ class CustomValidator(Validator):
529
563
  '2.5MW' -> 2500000.0
530
564
  Returns float (watts) or raises ValueError if invalid.
531
565
  """
532
- import re
533
566
  if value is None:
534
567
  return None
535
568
  if isinstance(value, (int, float)):
@@ -585,6 +618,44 @@ class CustomValidator(Validator):
585
618
  _LOGGER.debug(f"Parsed power value '{value}' as {result} W")
586
619
  return result
587
620
 
621
+ def _normalize_coerce_volume_flow_rate_to_lph(self, value):
622
+ """
623
+ Parse a volume flow rate value and return it in liters per minute (L/min).
624
+ Accepts:
625
+ - Numeric values (int, float)
626
+ - Strings with units: 'L/min', 'L/h', etc.
627
+ Example:
628
+ 9 -> 9.0
629
+ '9L/min' -> 9.0
630
+ '1L/h' -> 1000.0
631
+ Returns float (L/h) or raises ValueError if invalid.
632
+ """
633
+ if value is None:
634
+ return None
635
+ if isinstance(value, (int, float)):
636
+ return float(value)
637
+ if not isinstance(value, str):
638
+ raise ValueError(f"Unsupported type for volume flow rate value: {type(value)}")
639
+ value = value.strip().replace(' ', '').lower()
640
+ pattern = r"^([-+]?[0-9]*\.?[0-9]+)\s*([a-zA-Z/]*)$"
641
+ match = re.match(pattern, value)
642
+ if not match:
643
+ _LOGGER.warning(f"Could not parse volume flow rate value: {value}")
644
+ raise ValueError(f"Could not parse volume flow rate value: {value}")
645
+ num, unit = match.groups()
646
+ num = float(num)
647
+ multiplier = 1.0
648
+ if unit in ('lph', 'l/h', ''):
649
+ multiplier = 1.0
650
+ elif unit in ('lpm', 'l/min'):
651
+ multiplier = 60.0
652
+ else:
653
+ _LOGGER.warning(f"Unknown unit for volume flow rate value: {unit}")
654
+ raise ValueError(f"Unknown unit for volume flow rate value: {unit}")
655
+ result = num * multiplier
656
+ _LOGGER.debug(f"Parsed volume flow rate value '{value}' as {result} L/h")
657
+ return result
658
+
588
659
 
589
660
 
590
661
  def load_config_from_string(config_str: str) -> dict:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import logging
5
5
  from collections import deque
6
- from typing import Callable, Coroutine, List, Optional, Set
6
+ from typing import Callable, Coroutine, List, Optional, Set, Tuple
7
7
 
8
8
  from board import SCL, SDA
9
9
  from busio import I2C
@@ -15,6 +15,7 @@ from boneio.const import (
15
15
  BUTTON,
16
16
  CLOSE,
17
17
  COVER,
18
+ COVER_OVER_MQTT,
18
19
  DALLAS,
19
20
  DS2482,
20
21
  EVENT_ENTITY,
@@ -30,12 +31,12 @@ from boneio.const import (
30
31
  MODBUS,
31
32
  MQTT,
32
33
  NONE,
33
- OFF,
34
34
  ON,
35
35
  ONEWIRE,
36
36
  ONLINE,
37
37
  OPEN,
38
38
  OUTPUT,
39
+ OUTPUT_OVER_MQTT,
39
40
  PCA,
40
41
  PCF,
41
42
  PIN,
@@ -45,7 +46,6 @@ from boneio.const import (
45
46
  STATE,
46
47
  STOP,
47
48
  SWITCH,
48
- TOGGLE,
49
49
  TOPIC,
50
50
  UART,
51
51
  UARTS,
@@ -88,6 +88,7 @@ from boneio.helper.yaml_util import load_config_from_file
88
88
  from boneio.message_bus import MessageBus
89
89
  from boneio.modbus.client import Modbus
90
90
  from boneio.models import OutputState
91
+ from boneio.relay.basic import BasicRelay
91
92
  from boneio.sensor.temp import TempSensor
92
93
 
93
94
  _LOGGER = logging.getLogger(__name__)
@@ -126,7 +127,6 @@ class Manager:
126
127
  cover: list = [],
127
128
  web_active: bool = False,
128
129
  web_port: int = 8090,
129
- network_state: Optional[dict] = None,
130
130
  ) -> None:
131
131
  """Initialize the manager."""
132
132
  _LOGGER.info("Initializing manager module.")
@@ -140,7 +140,6 @@ class Manager:
140
140
  self._event_bus = event_bus
141
141
  self._is_web_on = web_active
142
142
  self._web_bind_port = web_port
143
- self._network_info = network_state
144
143
 
145
144
  self._message_bus: MessageBus = message_bus
146
145
 
@@ -153,7 +152,7 @@ class Manager:
153
152
  self._mcp = {}
154
153
  self._pcf = {}
155
154
  self._pca = {}
156
- self._outputs = {}
155
+ self._outputs: dict[str, BasicRelay] = {}
157
156
  self._configured_output_groups = {}
158
157
  self._interlock_manager = SoftwareInterlockManager()
159
158
 
@@ -177,13 +176,13 @@ class Manager:
177
176
  dallas=dallas, ds2482=ds2482, sensors=sensors.get(ONEWIRE)
178
177
  )
179
178
 
180
- self.grouped_outputs = create_expander(
179
+ self.grouped_outputs_by_expander = create_expander(
181
180
  expander_dict=self._mcp,
182
181
  expander_config=mcp23017,
183
182
  exp_type=MCP,
184
183
  i2cbusio=self._i2cbusio,
185
184
  )
186
- self.grouped_outputs.update(
185
+ self.grouped_outputs_by_expander.update(
187
186
  create_expander(
188
187
  expander_dict=self._pcf,
189
188
  expander_config=pcf8575,
@@ -191,7 +190,7 @@ class Manager:
191
190
  i2cbusio=self._i2cbusio,
192
191
  )
193
192
  )
194
- self.grouped_outputs.update(
193
+ self.grouped_outputs_by_expander.update(
195
194
  create_expander(
196
195
  expander_dict=self._pca,
197
196
  expander_config=pca9685,
@@ -206,6 +205,7 @@ class Manager:
206
205
  _name = _config.pop(ID)
207
206
  restore_state = _config.pop(RESTORE_STATE, False)
208
207
  _id = strip_accents(_name)
208
+ _LOGGER.debug("Configuring relay: %s", _id)
209
209
  out = configure_relay( # grouped_output updated here.
210
210
  manager=self,
211
211
  message_bus=message_bus,
@@ -237,7 +237,8 @@ class Manager:
237
237
  )
238
238
  self._loop.create_task(self._delayed_send_state(out))
239
239
 
240
- self._configure_covers()
240
+ if self._outputs:
241
+ self._configure_covers()
241
242
 
242
243
  self._outputs_group = output_group
243
244
  self._configure_output_group()
@@ -262,7 +263,7 @@ class Manager:
262
263
  manager=self,
263
264
  event_bus=self._event_bus,
264
265
  enabled_screens=self._screens,
265
- output=self.grouped_outputs,
266
+ output=self.grouped_outputs_by_expander,
266
267
  inputs=self._inputs,
267
268
  temp_sensor=(self._temp_sensors[0] if self._temp_sensors else None),
268
269
  ina219=(self._ina219_sensors[0] if self._ina219_sensors else None),
@@ -272,7 +273,7 @@ class Manager:
272
273
  self._oled = Oled(
273
274
  host_data=self._host_data,
274
275
  screen_order=self._screens,
275
- grouped_outputs=list(self.grouped_outputs),
276
+ grouped_outputs_by_expander=list(self.grouped_outputs_by_expander),
276
277
  sleep_timeout=oled.get("screensaver_timeout", 60),
277
278
  event_bus=self._event_bus,
278
279
  )
@@ -722,19 +723,21 @@ class Manager:
722
723
  """Get PCF by it's id."""
723
724
  return self._pcf
724
725
 
725
- def get_output_and_action(self, device_id, action, action_output, action_cover):
726
+ def get_output_and_action(
727
+ self, entity_id: str, action: str, action_output: str, action_cover: str
728
+ ) -> Tuple[BasicRelay, str]:
726
729
  """Get output device and its action based on configuration."""
727
730
  if action == OUTPUT:
728
731
  return (
729
732
  self._outputs.get(
730
- strip_accents(device_id),
731
- self._configured_output_groups.get(device_id),
733
+ strip_accents(entity_id),
734
+ self._configured_output_groups.get(entity_id),
732
735
  ),
733
736
  relay_actions.get(action_output),
734
737
  )
735
738
  else:
736
739
  return (
737
- self._covers.get(strip_accents(device_id)),
740
+ self._covers.get(strip_accents(entity_id)),
738
741
  cover_actions.get(action_cover),
739
742
  )
740
743
 
@@ -751,6 +754,32 @@ class Manager:
751
754
  actions = gpio.get_actions_of_click(click_type=x)
752
755
  topic = f"{self._config_helper.topic_prefix}/{gpio.input_type}/{gpio.pin}"
753
756
 
757
+ async def invoke_action_on_output(
758
+ entity_id: str, action_output: str, action: str, action_cover: str
759
+ ):
760
+ output, action_to_execute = self.get_output_and_action(
761
+ entity_id=entity_id,
762
+ action=action,
763
+ action_output=action_output,
764
+ action_cover=action_cover,
765
+ )
766
+ if output and action_to_execute:
767
+ _LOGGER.debug(
768
+ "Executing action %s for output %s", action_to_execute, output.name
769
+ )
770
+ _f = getattr(output, action_to_execute)
771
+ if _f:
772
+ await _f(**extra_data)
773
+ else:
774
+ if not action_to_execute:
775
+ _LOGGER.warning(
776
+ "Action doesn't exists %s, %s, %s. Check spelling",
777
+ action_definition,
778
+ actions,
779
+ )
780
+ if not output:
781
+ _LOGGER.warning("Device %s for action not found", entity_id)
782
+
754
783
  def generate_payload():
755
784
  if gpio.input_type == INPUT:
756
785
  if duration:
@@ -759,48 +788,47 @@ class Manager:
759
788
  return x
760
789
 
761
790
  for action_definition in actions:
762
- device_id = action_definition.get("pin")
791
+ entity_id = action_definition.get("pin")
763
792
  action = action_definition.get("action")
764
793
  action_output = action_definition.get("action_output")
765
794
  action_cover = action_definition.get("action_cover")
795
+ extra_data = action_definition.get("data", {})
766
796
  if action == MQTT:
767
797
  action_topic = action_definition.get(TOPIC)
768
798
  action_payload = action_definition.get("action_mqtt_msg")
769
- action_boneio = action_definition.get("action_mqtt_boneio")
770
- action_output = action_definition.get("action_output")
771
- pin = action_definition.get("pin")
772
- if action_boneio and pin and action_output in (TOGGLE, ON, OFF):
773
- self.send_message(
774
- topic=f"{action_boneio}/cmd/relay{pin}",
775
- payload=action_output,
776
- retain=False,
777
- )
778
799
  if action_topic and action_payload:
779
800
  self.send_message(
780
801
  topic=action_topic, payload=action_payload, retain=False
781
802
  )
782
803
  continue
783
- extra_data = action_definition.get("data", {})
784
- output, action = self.get_output_and_action(
785
- device_id=device_id,
786
- action=action,
787
- action_output=action_output,
788
- action_cover=action_cover,
789
- )
790
- if output and action:
791
- _LOGGER.debug("Executing action %s for output %s", action, output.name)
792
- _f = getattr(output, action)
793
- if _f:
794
- await _f(**extra_data)
795
- else:
796
- if not action:
797
- _LOGGER.warning(
798
- "Action doesn't exists %s, %s, %s. Check spelling",
799
- action_definition,
800
- actions,
804
+ elif action == OUTPUT or action == COVER:
805
+ await invoke_action_on_output(
806
+ entity_id=entity_id,
807
+ action_output=action_output,
808
+ action=action,
809
+ action_cover=action_cover,
810
+ )
811
+ elif action == OUTPUT_OVER_MQTT:
812
+ boneio_id = action_definition.get("boneio_id")
813
+ if relay_actions.get(action_output.upper()):
814
+ self.send_message(
815
+ topic=f"{boneio_id}/cmd/relay/{entity_id}/set",
816
+ payload=action_output,
817
+ retain=False,
801
818
  )
802
- if not output:
803
- _LOGGER.warning("Device %s for action not found", device_id)
819
+ else:
820
+ _LOGGER.warning("Desired action %s does not exists", action_output)
821
+ elif action == COVER_OVER_MQTT:
822
+ boneio_id = action_definition.get("boneio_id")
823
+ if cover_actions.get(action_cover.upper()):
824
+ self.send_message(
825
+ topic=f"{boneio_id}/cmd/cover/{entity_id}/set",
826
+ payload=action_cover,
827
+ retain=False,
828
+ )
829
+ else:
830
+ _LOGGER.warning("Desired action %s does not exists", action_cover)
831
+
804
832
  payload = generate_payload()
805
833
  _LOGGER.debug("Sending message %s for input %s", payload, topic)
806
834
  self.send_message(topic=topic, payload=payload, retain=False)
@@ -976,13 +1004,12 @@ class Manager:
976
1004
  if not self._config_helper.ha_discovery:
977
1005
  return
978
1006
  topic_prefix = topic_prefix or self._config_helper.topic_prefix
979
- if self._host_data:
980
- web_url = self._host_data.web_url
981
- else:
982
- if self._network_info and IP in self._network_info:
983
- web_url = f"http://{self._network_info[IP]}:{self._web_bind_port}"
984
- else:
985
- web_url = None
1007
+ web_url = None
1008
+ if self._config_helper.is_web_active:
1009
+ if self._host_data:
1010
+ web_url = self._host_data.web_url
1011
+ elif self._config_helper.network_info and IP in self._config_helper.network_info:
1012
+ web_url = f"http://{self._config_helper.network_info[IP]}:{self._web_bind_port}"
986
1013
  payload = availability_msg_func(
987
1014
  topic=topic_prefix,
988
1015
  id=id,
@@ -128,6 +128,7 @@ class MQTTClient(MessageBus):
128
128
  params["properties"] = properties
129
129
 
130
130
  # e.g. subscribe([("my/topic", SubscribeOptions(qos=0), ("another/topic", SubscribeOptions(qos=2)])
131
+ _LOGGER.debug("Subscribing to %s", args)
131
132
  await self.asyncio_client.subscribe(
132
133
  topic=args, **params, timeout=timeout
133
134
  )
@@ -250,9 +251,8 @@ class MQTTClient(MessageBus):
250
251
  cancel_task = asyncio.create_task(wait_for_cancel())
251
252
  tasks.add(cancel_task)
252
253
 
253
- await self.subscribe(topics=self._topics)
254
- await self.subscribe(topics=list(self._mqtt_energy_listeners.keys()))
255
- await self.subscribe(topics=self._discovery_topics)
254
+ topics = self._topics + list(self._mqtt_energy_listeners.keys()) + self._discovery_topics
255
+ await self.subscribe(topics=topics)
256
256
 
257
257
  # Wait for everything to complete (or fail due to, e.g., network errors).
258
258
  await asyncio.gather(*tasks)