boneio 0.9.0.dev7__tar.gz → 0.9.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 (128) hide show
  1. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/PKG-INFO +1 -1
  2. boneio-0.9.0.dev8/boneio/boards/0.8/input.yaml +110 -0
  3. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/events.py +75 -23
  4. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/gpio.py +5 -1
  5. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/ha_discovery.py +3 -3
  6. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/loader.py +35 -12
  7. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/stats.py +32 -12
  8. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/manager.py +42 -24
  9. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/sensor.py +4 -1
  10. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/models/__init__.py +9 -1
  11. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/oled.py +82 -27
  12. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/relay/basic.py +10 -5
  13. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/relay/mcp.py +0 -5
  14. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/relay/pcf.py +1 -7
  15. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/schema.yaml +5 -0
  16. boneio-0.9.0.dev8/boneio/sensor/serial_number.py +39 -0
  17. boneio-0.9.0.dev8/boneio/version.py +2 -0
  18. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/app.py +7 -9
  19. boneio-0.9.0.dev7/boneio/webui/frontend-dist/assets/index-JxJAopze.js → boneio-0.9.0.dev8/boneio/webui/frontend-dist/assets/index-Bzix_ULo.js +14 -14
  20. boneio-0.9.0.dev7/boneio/webui/frontend-dist/assets/index-Bs_Dk8Uy.css → boneio-0.9.0.dev8/boneio/webui/frontend-dist/assets/index-Dt_UdFzp.css +1 -1
  21. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/frontend-dist/index.html +2 -2
  22. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/web_server.py +2 -0
  23. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/pyproject.toml +2 -1
  24. boneio-0.9.0.dev7/boneio/version.py +0 -2
  25. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/LICENSE +0 -0
  26. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/README.md +0 -0
  27. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/__init__.py +0 -0
  28. {boneio-0.9.0.dev7/boneio/boards/0.8 → boneio-0.9.0.dev8/boneio/boards/0.7}/input.yaml +0 -0
  29. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/boards/0.7/output_32_10.yaml +0 -0
  30. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/boards/0.8/output_32_10.yaml +0 -0
  31. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/bonecli.py +0 -0
  32. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/const.py +0 -0
  33. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/cover.py +0 -0
  34. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/__init__.py +0 -0
  35. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/adc.yaml +0 -0
  36. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/binary_sensor.yaml +0 -0
  37. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/binary_sensor_v_0_7.yaml +0 -0
  38. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/config.yaml +0 -0
  39. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/cover.yaml +0 -0
  40. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/event.yaml +0 -0
  41. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/event_all.yaml +0 -0
  42. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/event_v_0_7.yaml +0 -0
  43. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/led32x4A.yaml +0 -0
  44. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/output24x16A.yaml +0 -0
  45. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/output24x16A_v0.3.yaml +0 -0
  46. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/output32x10A.yaml +0 -0
  47. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/example_config/output32x5A.yaml +0 -0
  48. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/fonts/danube__.ttf +0 -0
  49. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/group/__init__.py +0 -0
  50. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/group/output.py +0 -0
  51. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/__init__.py +0 -0
  52. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/async_updater.py +0 -0
  53. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/click_timer.py +0 -0
  54. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/config.py +0 -0
  55. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/exceptions.py +0 -0
  56. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/filter.py +0 -0
  57. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/gpiod.py +0 -0
  58. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/logger.py +0 -0
  59. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/message_bus.py +0 -0
  60. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/mqtt.py +0 -0
  61. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/oled.py +0 -0
  62. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/onewire/W1ThermSensor.py +0 -0
  63. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/onewire/__init__.py +0 -0
  64. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/onewire/ds2482.py +0 -0
  65. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/onewire/onewire.py +0 -0
  66. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/pcf8575.py +0 -0
  67. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/queue.py +0 -0
  68. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/sensor/ina_219_smbus.py +0 -0
  69. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/state_manager.py +0 -0
  70. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/timeperiod.py +0 -0
  71. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/util.py +0 -0
  72. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/helper/yaml_util.py +0 -0
  73. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/input/__init__.py +0 -0
  74. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/input/gpio.py +0 -0
  75. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/input/gpio_new.py +0 -0
  76. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/__init__.py +0 -0
  77. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/client.py +0 -0
  78. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/cwt.json +0 -0
  79. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/dts1964_3f.json +0 -0
  80. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/liquid-sensor.json +0 -0
  81. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/modbuscli.py +0 -0
  82. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/orno-or-we-517.json +0 -0
  83. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/pt100.json +0 -0
  84. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/r4dcb08.json +0 -0
  85. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/sdm120.json +0 -0
  86. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/sdm630.json +0 -0
  87. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/sht20.json +0 -0
  88. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/sht30.json +0 -0
  89. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/single_sensor.py +0 -0
  90. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/socomec_e03.json +0 -0
  91. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/socomec_e23.json +0 -0
  92. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/sofar.json +0 -0
  93. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/modbus/utils.py +0 -0
  94. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/models/files.py +0 -0
  95. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/models/logs.py +0 -0
  96. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/mqtt_client.py +0 -0
  97. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/relay/__init__.py +0 -0
  98. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/relay/gpio.py +0 -0
  99. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/relay/pca.py +0 -0
  100. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/runner.py +0 -0
  101. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/actions.yaml +0 -0
  102. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/actions_sensor.yaml +0 -0
  103. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/actions_switch.yaml +0 -0
  104. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/filters.yaml +0 -0
  105. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/filters_adc.yaml +0 -0
  106. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/id.yaml +0 -0
  107. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/temp_unit.yaml +0 -0
  108. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/schema/update_interval.yaml +0 -0
  109. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/__init__.py +0 -0
  110. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/adc.py +0 -0
  111. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/gpio.py +0 -0
  112. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/gpio_new.py +0 -0
  113. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/ina219.py +0 -0
  114. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/temp/__init__.py +0 -0
  115. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/temp/dallas.py +0 -0
  116. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/temp/lm75.py +0 -0
  117. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/sensor/temp/mcp9808.py +0 -0
  118. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/frontend-dist/assets/css.worker-YzWC8yAR.js +0 -0
  119. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/frontend-dist/assets/editor.worker-D5ngByhL.js +0 -0
  120. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/frontend-dist/assets/html.worker-D5NPIcdM.js +0 -0
  121. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/frontend-dist/assets/json.worker-F0ZGA5Zk.js +0 -0
  122. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/frontend-dist/assets/ts.worker-BSchkg-h.js +0 -0
  123. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/frontend-dist/boneio.svg +0 -0
  124. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/frontend-dist/boneio_fav.svg +0 -0
  125. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/boneio/webui/websocket_manager.py +0 -0
  126. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/tests/32_5_config.yaml +0 -0
  127. {boneio-0.9.0.dev7 → boneio-0.9.0.dev8}/tests/bandit.yaml +0 -0
  128. {boneio-0.9.0.dev7 → boneio-0.9.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.9.0.dev7
3
+ Version: 0.9.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>
@@ -0,0 +1,110 @@
1
+ input_mapping:
2
+ in_01:
3
+ pin: P8_37
4
+ gpio_mode: gpio_pu
5
+ in_02:
6
+ pin: P8_38
7
+ gpio_mode: gpio_pu
8
+ in_03:
9
+ pin: P8_39
10
+ in_04:
11
+ pin: P8_40
12
+ in_05:
13
+ pin: P8_41
14
+ in_06:
15
+ pin: P8_42
16
+ in_07:
17
+ pin: P8_43
18
+ in_08:
19
+ pin: P8_44
20
+ in_09:
21
+ pin: P8_45
22
+ in_10:
23
+ pin: P8_46
24
+ gpio_mode: gpio_pu
25
+ in_11:
26
+ pin: P9_42
27
+ in_12:
28
+ pin: P9_31
29
+ in_13:
30
+ pin: P9_30
31
+ in_14:
32
+ pin: P9_29
33
+ in_15:
34
+ pin: P9_28
35
+ in_16:
36
+ pin: P9_27
37
+ in_17:
38
+ pin: P9_25
39
+ in_18:
40
+ pin: P9_23
41
+ in_19:
42
+ pin: P9_22
43
+ gpio_mode: gpio_pu
44
+ in_20:
45
+ pin: P9_21
46
+ gpio_mode: gpio_pu
47
+ in_21:
48
+ pin: P9_18
49
+ in_22:
50
+ pin: P9_17
51
+ in_23:
52
+ pin: P9_16
53
+ in_24:
54
+ pin: P9_15
55
+ in_25:
56
+ pin: P9_14
57
+ in_26:
58
+ pin: P8_7
59
+ in_27:
60
+ pin: P8_8
61
+ in_28:
62
+ pin: P8_9
63
+ in_29:
64
+ pin: P8_36
65
+ gpio_mode: gpio_pu
66
+ in_30:
67
+ pin: P8_35
68
+ gpio_mode: gpio_pu
69
+ in_31:
70
+ pin: P8_34
71
+ gpio_mode: gpio_pu
72
+ in_32:
73
+ pin: P8_33
74
+ gpio_mode: gpio_pu
75
+ in_33:
76
+ pin: P8_32
77
+ gpio_mode: gpio_pu
78
+ in_34:
79
+ pin: P8_31
80
+ gpio_mode: gpio_pu
81
+ in_35:
82
+ pin: P8_30
83
+ in_36:
84
+ pin: P8_29
85
+ in_37:
86
+ pin: P8_28
87
+ in_38:
88
+ pin: P8_27
89
+ in_39:
90
+ pin: P8_26
91
+ in_40:
92
+ pin: P8_19
93
+ in_41:
94
+ pin: P8_18
95
+ in_42:
96
+ pin: P8_17
97
+ in_43:
98
+ pin: P8_16
99
+ in_44:
100
+ pin: P8_15
101
+ in_45:
102
+ pin: P8_14
103
+ in_46:
104
+ pin: P8_13
105
+ in_47:
106
+ pin: P8_12
107
+ in_48:
108
+ pin: P8_11
109
+ in_49:
110
+ pin: P8_10
@@ -86,7 +86,15 @@ class EventBus:
86
86
  self._loop = loop or asyncio.get_event_loop()
87
87
  self._every_second_listeners = {}
88
88
  self._output_listeners = {}
89
- self._event_listeners = {}
89
+ self._event_listeners = {
90
+ "input": {},
91
+ "output": {},
92
+ "modbus_sensor": {},
93
+ "sensor": {},
94
+ "host": {}
95
+ }
96
+ # Index to track listener_ids across all event types
97
+ self._listener_id_index = {}
90
98
  self._sigterm_listeners = []
91
99
  self._haonline_listeners = []
92
100
  self._timer_handle = _async_create_timer(
@@ -188,7 +196,7 @@ class EventBus:
188
196
  """Add sigterm listener."""
189
197
  self._sigterm_listeners.append(target)
190
198
 
191
- def add_output_listener(self, output_id, listener_id, target):
199
+ def add_output_listener(self, output_id, listener_id, target) -> ListenerJob:
192
200
  """Add output listener.
193
201
 
194
202
  listener_id is typically group_id for group outputs or ws (websocket)
@@ -200,7 +208,7 @@ class EventBus:
200
208
  listener.set_target(target)
201
209
  return listener
202
210
  self._output_listeners[output_id][listener_id] = ListenerJob(target=target)
203
- return self._output_listeners[output_id]
211
+ return self._output_listeners[output_id][listener_id]
204
212
 
205
213
  def remove_output_listener(self, output_id, listener_id):
206
214
  """Add output listener.
@@ -215,36 +223,80 @@ class EventBus:
215
223
  for listener in listeners.values():
216
224
  await listener.target(event)
217
225
 
218
- def add_event_listener(self, event_type: str, entity_id: str, listener_id: str, target: Callable) -> ListenerJob:
219
- """Add output listener.
226
+ async def async_trigger_event(self, event_type: str, entity_id: str, event: Any):
227
+ """Trigger event for all listeners of given type and entity."""
228
+ listeners = self._event_listeners.get(event_type, {}).get(entity_id, {})
229
+ for listener in listeners.values():
230
+ await listener.target(event)
231
+
232
+ def add_event_listener(
233
+ self, event_type: str, entity_id: str, listener_id: str, target
234
+ ) -> ListenerJob:
235
+ """Add event listener.
220
236
 
221
237
  listener_id is typically group_id for group outputs or ws (websocket)
222
238
  """
223
- if event_type not in self._event_listeners:
224
- self._event_listeners[event_type] = {}
239
+ if not target:
240
+ return
225
241
  if entity_id not in self._event_listeners[event_type]:
226
242
  self._event_listeners[event_type][entity_id] = {}
227
243
  if listener_id in self._event_listeners[event_type][entity_id]:
228
244
  listener = self._event_listeners[event_type][entity_id][listener_id]
229
245
  listener.set_target(target)
230
246
  return listener
231
- self._event_listeners[event_type][entity_id][listener_id] = ListenerJob(target=target)
232
- return self._event_listeners[event_type][entity_id][listener_id]
233
247
 
234
- def remove_event_listener(self, event_type: str, entity_id: str, listener_id: str) -> None:
235
- """Remove event listener."""
236
- if event_type in self._event_listeners and entity_id in self._event_listeners[event_type] and listener_id in self._event_listeners[event_type][entity_id]:
237
- del self._event_listeners[event_type][entity_id][listener_id]
238
-
239
- def remove_event_listener_by_type(self, event_type: str) -> None:
240
- """Remove event listener."""
241
- if event_type in self._event_listeners:
242
- del self._event_listeners[event_type]
243
-
244
- async def async_trigger_event(self, event_type: str, entity_id: str, event: Any):
245
- listeners = self._event_listeners.get(event_type, {}).get(entity_id, {})
246
- for listener in listeners.values():
247
- await listener.target(event)
248
+ # Add to main listeners
249
+ listener_job = ListenerJob(target=target)
250
+ self._event_listeners[event_type][entity_id][listener_id] = listener_job
251
+
252
+ # Add to index
253
+ if listener_id not in self._listener_id_index:
254
+ self._listener_id_index[listener_id] = []
255
+ self._listener_id_index[listener_id].append((event_type, entity_id))
256
+
257
+ return listener_job
258
+
259
+ def remove_event_listener(self, event_type: str = None, entity_id: str = None, listener_id: str = None) -> None:
260
+ """Remove event listener. Can remove by event_type, listener_id, or both."""
261
+ if listener_id and listener_id in self._listener_id_index:
262
+ # Remove by listener_id
263
+ for evt_type, ent_id in self._listener_id_index[listener_id]:
264
+ if event_type and evt_type != event_type:
265
+ continue
266
+ if entity_id and ent_id != entity_id:
267
+ continue
268
+ if ent_id in self._event_listeners[evt_type]:
269
+ if listener_id in self._event_listeners[evt_type][ent_id]:
270
+ del self._event_listeners[evt_type][ent_id][listener_id]
271
+ if not self._event_listeners[evt_type][ent_id]:
272
+ del self._event_listeners[evt_type][ent_id]
273
+
274
+ if event_type is None and entity_id is None:
275
+ # If removing all references to this listener_id
276
+ del self._listener_id_index[listener_id]
277
+ else:
278
+ # Update index to remove only specific references
279
+ self._listener_id_index[listener_id] = [
280
+ (evt_type, ent_id)
281
+ for evt_type, ent_id in self._listener_id_index[listener_id]
282
+ if (event_type and evt_type != event_type) or (entity_id and ent_id != entity_id)
283
+ ]
284
+ if not self._listener_id_index[listener_id]:
285
+ del self._listener_id_index[listener_id]
286
+
287
+ elif event_type:
288
+ # Remove by event_type
289
+ if entity_id:
290
+ # Remove specific entity
291
+ if entity_id in self._event_listeners[event_type]:
292
+ for lid in list(self._event_listeners[event_type][entity_id].keys()):
293
+ self.remove_event_listener(event_type, entity_id, lid)
294
+ del self._event_listeners[event_type][entity_id]
295
+ else:
296
+ # Remove entire event_type
297
+ for ent_id in list(self._event_listeners[event_type].keys()):
298
+ for lid in list(self._event_listeners[event_type][ent_id].keys()):
299
+ self.remove_event_listener(event_type, ent_id, lid)
248
300
 
249
301
  def add_haonline_listener(self, target: Callable) -> None:
250
302
  """Add HA Online listener."""
@@ -194,7 +194,7 @@ class GpioBaseClass:
194
194
  timestamp=self.last_press_timestamp,
195
195
  boneio_input=self.boneio_input,
196
196
  )
197
- await self._event_bus.async_trigger_event(event_type="input", entity_id=self._pin, event=event)
197
+ await self._event_bus.async_trigger_event(event_type="input", entity_id=self.id, event=event)
198
198
 
199
199
  def set_actions(self, actions: dict) -> None:
200
200
  self._actions = actions
@@ -222,6 +222,10 @@ class GpioBaseClass:
222
222
  """Return configured pin."""
223
223
  return self._pin
224
224
 
225
+ @property
226
+ def id(self) -> str:
227
+ return self._pin
228
+
225
229
  @property
226
230
  def last_state(self) -> str:
227
231
  return self._last_state
@@ -3,19 +3,19 @@ from boneio.const import (
3
3
  CLOSED,
4
4
  CLOSING,
5
5
  COVER,
6
+ DOUBLE,
6
7
  INPUT,
7
8
  INPUT_SENSOR,
9
+ LONG,
8
10
  OFF,
9
11
  ON,
10
12
  OPEN,
11
13
  OPENING,
12
14
  RELAY,
13
15
  SENSOR,
16
+ SINGLE,
14
17
  STATE,
15
18
  STOP,
16
- SINGLE,
17
- DOUBLE,
18
- LONG
19
19
  )
20
20
  from boneio.version import __version__
21
21
 
@@ -56,7 +56,10 @@ from boneio.helper import (
56
56
  ha_sensor_temp_availabilty_message,
57
57
  )
58
58
  from boneio.helper.events import EventBus
59
- from boneio.helper.ha_discovery import ha_cover_availabilty_message
59
+ from boneio.helper.ha_discovery import (
60
+ ha_cover_availabilty_message,
61
+ ha_sensor_availabilty_message,
62
+ )
60
63
  from boneio.helper.onewire import (
61
64
  DS2482,
62
65
  DS2482_ADDRESS,
@@ -67,12 +70,12 @@ from boneio.helper.onewire import (
67
70
  from boneio.helper.pcf8575 import PCF8575
68
71
  from boneio.helper.timeperiod import TimePeriod
69
72
  from boneio.input import GpioEventButtonNew, GpioEventButtonOld
70
- from boneio.models import OutputState
71
73
  from boneio.sensor import (
72
74
  DallasSensorDS2482,
73
75
  GpioInputBinarySensorNew,
74
76
  GpioInputBinarySensorOld,
75
77
  )
78
+ from boneio.sensor.serial_number import SerialNumberSensor
76
79
  from boneio.sensor.temp.dallas import DallasSensorW1
77
80
 
78
81
  # Typing imports that create a circular dependency
@@ -166,6 +169,27 @@ def create_temp_sensor(
166
169
  pass
167
170
 
168
171
 
172
+ def create_serial_number_sensor(
173
+ manager: Manager,
174
+ topic_prefix: str,
175
+ ):
176
+ """Create Serial number sensor in manager."""
177
+ sensor = SerialNumberSensor(
178
+ id="serial_number",
179
+ name="Serial number",
180
+ manager=manager,
181
+ send_message=manager.send_message,
182
+ topic_prefix=topic_prefix,
183
+ )
184
+ manager.send_ha_autodiscovery(
185
+ id="serial_number",
186
+ name="Serial number",
187
+ ha_type=SENSOR,
188
+ entity_category="diagnostic",
189
+ availability_msg_func=ha_sensor_availabilty_message,
190
+ )
191
+ return sensor
192
+
169
193
  expander_class = {MCP: MCP23017, PCA: PCA9685, PCF: PCF8575}
170
194
 
171
195
 
@@ -275,12 +299,11 @@ def configure_relay(
275
299
  topic_prefix: str,
276
300
  relay_id: str,
277
301
  name: str,
278
- relay_callback: Callable,
279
302
  config: dict,
303
+ restore_state: bool = False,
280
304
  **kwargs,
281
305
  ) -> Any:
282
306
  """Configure kind of relay. Most common MCP."""
283
- restore_state = config.pop(RESTORE_STATE, False)
284
307
  output_type = config.pop(OUTPUT_TYPE)
285
308
  restored_state = (
286
309
  state_manager.get(attr_type=RELAY, attr=relay_id, default_value=False)
@@ -342,13 +365,13 @@ def configure_relay(
342
365
  )
343
366
  return
344
367
 
345
- # Create async callback wrapper
346
- async def relay_callback_wrapper(event: OutputState):
347
- await relay_callback(
348
- expander_id=getattr(output, "expander_id"),
349
- event=event,
350
- restore_state=False if output_type == NONE else restore_state,
351
- )
368
+ # # Create async callback wrapper
369
+ # async def relay_callback_wrapper(event: OutputState):
370
+ # await relay_callback(
371
+ # expander_id=expander_id,
372
+ # event=event,
373
+ # restore_state=False if output_type == NONE else restore_state,
374
+ # )
352
375
 
353
376
  relay = getattr(output, "OutputClass")(
354
377
  send_message=manager.send_message,
@@ -359,7 +382,7 @@ def configure_relay(
359
382
  **config,
360
383
  **kwargs,
361
384
  **extra_args,
362
- callback=relay_callback_wrapper,
385
+ # callback=relay_callback_wrapper,
363
386
  )
364
387
  manager.grouped_outputs[expander_id][relay_id] = relay
365
388
  return relay
@@ -4,7 +4,6 @@ import asyncio
4
4
  import logging
5
5
  import socket
6
6
  import time
7
- from functools import partial
8
7
  from math import floor
9
8
 
10
9
  # Typing imports that create a circular dependency
@@ -28,7 +27,9 @@ from boneio.const import (
28
27
  SWAP,
29
28
  UPTIME,
30
29
  )
30
+ from boneio.helper.events import EventBus
31
31
  from boneio.helper.gpio import GpioBaseClass
32
+ from boneio.models import HostSensorState
32
33
 
33
34
  if TYPE_CHECKING:
34
35
  from boneio.manager import Manager
@@ -124,8 +125,8 @@ class HostSensor(AsyncUpdater):
124
125
 
125
126
  def __init__(
126
127
  self,
128
+ event_bus: EventBus,
127
129
  update_function: Callable,
128
- manager_callback: Callable,
129
130
  static_data: dict | None,
130
131
  id: str,
131
132
  type: str,
@@ -135,16 +136,21 @@ class HostSensor(AsyncUpdater):
135
136
  self._static_data = static_data
136
137
  self._state = {}
137
138
  self._type = type
138
- self._manager_callback = manager_callback
139
+ self._event_bus = event_bus
139
140
  self._loop = asyncio.get_event_loop()
140
141
  self.id = id
141
142
  super().__init__(**kwargs)
143
+ self._loop.create_task(self.async_update(time.time()))
142
144
 
143
145
  async def async_update(self, timestamp: float) -> None:
144
146
  self._state = self._update_function()
145
- self._loop.call_soon_threadsafe(
146
- partial(self._manager_callback, self._type)
147
+ sensor_state = HostSensorState(
148
+ id=self.id,
149
+ name=self._type,
150
+ state="new_state", #doesn't matter here, as we fetch everything in Oled.
151
+ timestamp=timestamp,
147
152
  )
153
+ await self._event_bus.async_trigger_event(event_type="host", entity_id=self.id, event=sensor_state)
148
154
 
149
155
  @property
150
156
  def state(self) -> dict:
@@ -160,14 +166,15 @@ class HostData:
160
166
  self,
161
167
  output: dict,
162
168
  inputs: dict[str, GpioBaseClass],
163
- callback: Callable,
164
169
  temp_sensor: Callable[[LM75Sensor, MCP9808Sensor], None] | None,
165
170
  ina219: INA219Class | None,
166
171
  manager: Manager,
172
+ event_bus: EventBus,
167
173
  enabled_screens: List[str],
168
174
  extra_sensors: List[dict],
169
175
  ) -> None:
170
176
  """Initialize HostData."""
177
+ self._manager = manager
171
178
  self._hostname = socket.gethostname()
172
179
  self._temp_sensor = temp_sensor
173
180
  host_stats = {
@@ -293,8 +300,8 @@ class HostData:
293
300
  self._data[k] = HostSensor(
294
301
  update_function=_v["f"],
295
302
  static_data=_v.get("static"),
303
+ event_bus=event_bus,
296
304
  manager=manager,
297
- manager_callback=callback,
298
305
  id=f"{k}_hoststats",
299
306
  type=k,
300
307
  update_interval=_v["update_interval"],
@@ -306,31 +313,44 @@ class HostData:
306
313
  ]
307
314
  for i in range((len(inputs) + 24) // 25)
308
315
  }
309
- self._callback = callback
310
316
  self._loop = asyncio.get_running_loop()
311
317
 
318
+ def web_url(self) -> str | None:
319
+ if not self._manager.is_web_on:
320
+ return None
321
+ network_state = self._data[NETWORK].state
322
+ if IP in network_state:
323
+ return f"http://{network_state[IP]}:{self._manager.web_bind_port}"
324
+ return None
325
+
312
326
  def get(self, type: str) -> dict:
313
327
  """Get saved stats."""
314
328
  if type in self._output:
315
329
  return self._get_output(type)
316
330
  if type in self._inputs:
317
331
  return self._get_input(type)
332
+ if type == "web":
333
+ return self.web_url()
318
334
  return self._data[type].state
319
335
 
320
336
  def _get_output(self, type: str) -> dict:
321
337
  """Get stats for output."""
322
338
  out = {}
323
339
  for output in self._output[type].values():
324
- out[output.id] = output.state
340
+ out[output.id] = {
341
+ "name": output.name, "state": output.state
342
+ }
343
+
325
344
  return out
326
345
 
327
346
  def _get_input(self, type: str) -> dict:
328
347
  """Get stats for input."""
329
348
  inputs = {}
330
349
  for input in self._inputs[type]:
331
- inputs[input.name] = (
332
- input.pressed_state[0].upper() if input.pressed_state else ""
333
- )
350
+ inputs[input.id] = {
351
+ "name": input.name,
352
+ "state": input.last_state[0].upper() if input.last_state and input.last_state != "Unknown" else ""
353
+ }
334
354
  return inputs
335
355
 
336
356
  @property