ekfsm 1.1.0a15.post1__py3-none-any.whl → 1.3.0__py3-none-any.whl

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.

Potentially problematic release.


This version of ekfsm might be problematic. Click here for more details.

ekfsm/devices/generic.py CHANGED
@@ -1,12 +1,14 @@
1
- from typing import TYPE_CHECKING
1
+ from pathlib import Path
2
+ from typing import TYPE_CHECKING, Callable
3
+
2
4
  from munch import Munch
5
+
3
6
  from ekfsm.core.components import SysTree
4
- from ekfsm.core.sysfs import SysFSDevice, sysfs_root
5
- from ekfsm.exceptions import ConfigError
6
- from pathlib import Path
7
+ from ekfsm.core.sysfs import SysfsDevice, sysfs_root
8
+ from ekfsm.exceptions import ConfigError, SysFSError, UnsupportedModeError
7
9
 
8
10
  if TYPE_CHECKING:
9
- from ekfsm.core.components import HwModule
11
+ from ekfsm.core.components import HWModule
10
12
 
11
13
 
12
14
  class Device(SysTree):
@@ -26,26 +28,25 @@ class Device(SysTree):
26
28
  super().__init__(name, abort=abort)
27
29
  self.parent = parent
28
30
  self.device_args = kwargs
29
- self.logger.debug(f"Device: {name} {kwargs}")
30
31
 
31
32
  if children:
32
33
  self.children = children
33
34
 
34
- # needs to be set during init because root will be changed after tree is complete
35
- self.hw_module = self.root
35
+ # Needs to be set during init because root will be changed after tree is complete
36
+ self.hw_module = self.root # pyright: ignore[reportAttributeAccessIssue]
36
37
 
37
- # i2c initialization
38
+ # I2C initialization
38
39
  if not hasattr(self, "sysfs_device") or self.sysfs_device is None:
39
- self.sysfs_device: SysFSDevice | None = None
40
+ self.sysfs_device: SysfsDevice | None = None
40
41
 
41
- # post init
42
+ # Post-initialization steps
42
43
  self._provides_attrs = kwargs.get("provides", {})
43
- self.provides = self.__post_init__(Munch(self._provides_attrs))
44
+ self.provides = self.__post_init__(Munch(self._provides_attrs), abort)
44
45
 
45
- def __post_init__(self, provides: Munch) -> Munch:
46
+ def __post_init__(self, provides: Munch, abort) -> Munch:
46
47
  for key, fields in provides.items():
47
48
  if isinstance(fields, dict):
48
- provides[key] = self.__post_init__(Munch(fields))
49
+ provides[key] = self.__post_init__(Munch(fields), abort)
49
50
  elif isinstance(fields, list | str):
50
51
  provides[key] = Munch()
51
52
 
@@ -53,29 +54,85 @@ class Device(SysTree):
53
54
  fields = [fields]
54
55
 
55
56
  while fields:
56
- iface = fields.pop()
57
- if isinstance(iface, dict):
58
- name = list(iface.keys())[0]
57
+ interface = fields.pop()
58
+
59
+ # TODO: Check if this is superfluous after schema validation
60
+ if isinstance(interface, dict):
61
+ name = list(interface.keys())[0]
62
+
59
63
  try:
60
- func = list(iface.values())[0]
64
+ func = list(interface.values())[0]
61
65
  except IndexError:
62
- raise ConfigError(
63
- f"{self.name}: No function given for interface {name}."
64
- )
66
+ raise ConfigError(f"{self.name}: No function given for interface {name}.")
67
+
65
68
  if not hasattr(self, func):
66
- raise NotImplementedError(
67
- f"{self.name}: Function {func} for interface {name} not implemented."
68
- )
69
+ if abort:
70
+ raise NotImplementedError(
71
+ f"{self.name}: Function {func} for interface {name} not implemented."
72
+ )
73
+ continue
74
+
69
75
  provides[key].update({name: getattr(self, func)})
70
76
  else:
71
- if not hasattr(self, iface):
72
- raise NotImplementedError(
73
- f"{self.name}: Function {iface} for provider {key} not implemented."
74
- )
75
- provides[key].update({iface: getattr(self, iface)})
77
+ if not hasattr(self, interface):
78
+ if abort:
79
+ raise NotImplementedError(
80
+ f"{self.name}: Function {interface} for provider {key} not implemented."
81
+ )
82
+ continue
83
+
84
+ provides[key].update({interface: getattr(self, interface)})
76
85
 
77
86
  return provides
78
87
 
88
+ @property
89
+ def sysfs(self):
90
+ """
91
+ Access sysfs device for device
92
+
93
+ Returns
94
+ -------
95
+ Sysfs device for device
96
+
97
+ Raises
98
+ ------
99
+ SysFSError
100
+ If the SysFSDevice does not exist
101
+ """
102
+ if self.sysfs_device is None:
103
+ raise SysFSError(f"No sysfs device attached to device {self.name}")
104
+
105
+ return self.sysfs_device
106
+
107
+ def read_attr(self, attr: str, mode: str = "utf", strip: bool = True):
108
+ match mode:
109
+ case "utf":
110
+ return self.sysfs.read_utf8(attr, strip)
111
+ case "bytes":
112
+ return self.sysfs.read_bytes(attr)
113
+ case "float":
114
+ return self.sysfs.read_float(attr)
115
+ case "int":
116
+ return self.sysfs.read_int(attr)
117
+ case _:
118
+ raise UnsupportedModeError(f"Mode {mode} is not supported")
119
+
120
+ def read_attr_or_default(self, attr: str, mode: str = "utf", strip: bool = True, default=None):
121
+ try:
122
+ return self.read_attr(attr, mode, strip)
123
+ except UnsupportedModeError:
124
+ raise
125
+ except Exception:
126
+ if default is not None:
127
+ return default
128
+ return None
129
+
130
+ def read_sysfs_bytes(self, attr) -> bytes:
131
+ if len(attr) <= 0:
132
+ raise SysFSError("sysfs attribute name too short")
133
+
134
+ return self.sysfs.read_bytes(attr)
135
+
79
136
  def read_sysfs_attr_bytes(self, attr: str) -> bytes | None:
80
137
  """
81
138
  Read a sysfs attribute as bytes.
@@ -92,7 +149,7 @@ class Device(SysTree):
92
149
  None:
93
150
  If the sysfs device is not set or the attribute does not exist.
94
151
  """
95
- if self.sysfs_device and len(attr) != 0:
152
+ if self.sysfs_device is not None and len(attr) != 0 and attr in [x.name for x in self.sysfs_device.attributes]:
96
153
  return self.sysfs_device.read_attr_bytes(attr)
97
154
  return None
98
155
 
@@ -112,9 +169,10 @@ class Device(SysTree):
112
169
  None:
113
170
  If the sysfs device is not set or the attribute does not exist.
114
171
  """
115
- if self.sysfs_device and len(attr) != 0:
116
- return self.sysfs_device.read_attr_utf8(attr)
117
- return None
172
+ try:
173
+ return self.sysfs.read_utf8(attr)
174
+ except Exception:
175
+ return None
118
176
 
119
177
  def write_sysfs_attr(self, attr: str, data: str | bytes) -> None:
120
178
  """
@@ -132,31 +190,31 @@ class Device(SysTree):
132
190
  return None
133
191
 
134
192
  @property
135
- def hw_module(self) -> "HwModule":
193
+ def hw_module(self) -> "HWModule":
136
194
  """
137
- Get or set the HwModule instance that this device belongs to.
195
+ Get or set the HWModule instance that this device belongs to.
138
196
 
139
197
  Parameters
140
198
  ----------
141
199
  hw_module: optional
142
- The HwModule instance to set.
200
+ The HWModule instance to set.
143
201
 
144
202
  Returns
145
203
  -------
146
- HwModule
147
- The HwModule instance that this device belongs to.
204
+ HWModule
205
+ The HWModule instance that this device belongs to.
148
206
  None
149
207
  If used as a setter.
150
208
  """
151
- from ekfsm.core.components import HwModule
209
+ from ekfsm.core.components import HWModule
152
210
 
153
- if isinstance(self._hw_module, HwModule):
211
+ if isinstance(self._hw_module, HWModule):
154
212
  return self._hw_module
155
213
  else:
156
- raise RuntimeError("Device is not a child of HwModule")
214
+ raise RuntimeError("Device is not a child of HWModule")
157
215
 
158
216
  @hw_module.setter
159
- def hw_module(self, hw_module: "HwModule") -> None:
217
+ def hw_module(self, hw_module: "HWModule") -> None:
160
218
  self._hw_module = hw_module
161
219
 
162
220
  def get_i2c_chip_addr(self) -> int:
@@ -164,9 +222,7 @@ class Device(SysTree):
164
222
 
165
223
  chip_addr = self.device_args.get("addr")
166
224
  if chip_addr is None:
167
- raise ConfigError(
168
- f"{self.name}: Chip address not provided in board definition"
169
- )
225
+ raise ConfigError(f"{self.name}: Chip address not provided in board definition")
170
226
 
171
227
  if not hasattr(self.parent, "sysfs_device") or self.parent.sysfs_device is None:
172
228
  # our device is the top level device of the slot
@@ -174,16 +230,12 @@ class Device(SysTree):
174
230
  slot_attributes = self.hw_module.slot.attributes
175
231
 
176
232
  if slot_attributes is None:
177
- raise ConfigError(
178
- f"{self.name}: Slot attributes not provided in system configuration"
179
- )
233
+ raise ConfigError(f"{self.name}: Slot attributes not provided in system configuration")
180
234
 
181
235
  if not self.hw_module.is_master:
182
236
  # slot coding is only used for non-master devices
183
237
  if not hasattr(slot_attributes, "slot_coding"):
184
- raise ConfigError(
185
- f"{self.name}: Slot coding not provided in slot attributes"
186
- )
238
+ raise ConfigError(f"{self.name}: Slot coding not provided in slot attributes")
187
239
 
188
240
  slot_coding_mask = 0xFF
189
241
 
@@ -194,15 +246,16 @@ class Device(SysTree):
194
246
 
195
247
  return chip_addr
196
248
 
197
- def get_i2c_sysfs_device(self, addr: int) -> SysFSDevice:
198
- from ekfsm.core.components import HwModule
249
+ def get_i2c_sysfs_device(self, addr: int, driver_required=True, find_driver: Callable | None = None) -> SysfsDevice:
250
+ from ekfsm.core.components import HWModule
199
251
 
200
252
  parent = self.parent
201
253
  assert parent is not None
202
254
 
203
- # if parent is a HwModule, we can get the i2c bus from the master device
204
- if isinstance(parent, HwModule):
205
- i2c_bus_path = self._master_i2c_bus()
255
+ # If parent is a HWModule, we can get the i2c bus from the master device
256
+ # XXX: Does this still hold true after refactoring?
257
+ if isinstance(parent, HWModule):
258
+ i2c_bus_path = self.__master_i2c_bus()
206
259
  else:
207
260
  # otherwise the parent must be a MuxChannel
208
261
  from ekfsm.devices.mux import MuxChannel
@@ -218,67 +271,64 @@ class Device(SysTree):
218
271
  and not (entry / "new_device").exists() # skip bus entries
219
272
  and (entry / "name").exists()
220
273
  ):
221
- # for PRP devices, address is contained in firmware_node/description
222
- if (entry / "firmware_node").exists() and (
223
- entry / "firmware_node" / "description"
224
- ).exists():
225
- description = (
226
- (entry / "firmware_node/description").read_text().strip()
227
- )
228
- got_addr = int(description.split(" - ")[0], 16)
229
- if got_addr == addr:
230
- return SysFSDevice(entry)
231
-
232
- # for non-PRP devices, address is contained in the directory name (e.g. 2-0018)
274
+ # PRP devices unfortunately do not readily expose the underlying I2C address of the device like
275
+ # regular I2C devices that follow the `${I2C_BUS}-${ADDR}` pattern. To address this issue, we
276
+ # initialize the ACPI _STR object for each PRP device with the necessary information, which is
277
+ # accessible in the `${DEVICE_SYSFS_PATH}/firmware_node/description` file.
278
+ if (entry / "firmware_node").exists() and (entry / "firmware_node" / "description").exists():
279
+ description = (entry / "firmware_node/description").read_text().strip()
280
+ acpi_addr = int(description.split(" - ")[0], 16)
281
+
282
+ if acpi_addr == addr:
283
+ return SysfsDevice(entry, driver_required, find_driver)
284
+
285
+ # For regular non-PRP devices, the address is contained in the directory name (e.g. 2-0018).
233
286
  else:
234
- got_addr = int(entry.name.split("-")[1], 16)
235
- if got_addr == addr:
236
- return SysFSDevice(entry)
287
+ acpi_addr = int(entry.name.split("-")[1], 16)
237
288
 
238
- raise FileNotFoundError(
239
- f"Device with address 0x{addr:x} not found in {i2c_bus_path}"
240
- )
289
+ if acpi_addr == addr:
290
+ return SysfsDevice(entry, driver_required, find_driver)
291
+
292
+ raise FileNotFoundError(f"Device with address 0x{addr:x} not found in {i2c_bus_path}")
241
293
 
242
294
  @staticmethod
243
- def _master_i2c_get_config(master: "HwModule") -> dict:
244
- if (
245
- master.config.get("bus_masters") is not None
246
- and master.config["bus_masters"].get("i2c") is not None
247
- ):
295
+ def __master_i2c_get_config(master: "HWModule") -> dict:
296
+ if master.config.get("bus_masters") is not None and master.config["bus_masters"].get("i2c") is not None:
248
297
  return master.config["bus_masters"]["i2c"]
249
298
  else:
250
299
  raise ConfigError("Master definition incomplete")
251
300
 
252
- def _master_i2c_bus(self) -> Path:
301
+ def __master_i2c_bus(self) -> Path:
253
302
  if self.hw_module.is_master:
254
303
  # we are the master
255
304
  master = self.hw_module
256
305
  master_key = "MASTER_LOCAL_DEFAULT"
257
306
  override_master_key = self.device_args.get("i2c_master", None)
307
+
258
308
  if override_master_key is not None:
259
309
  master_key = override_master_key
260
310
  else:
261
311
  # another board is the master
262
312
  if self.hw_module.slot.master is None:
263
- raise ConfigError(
264
- f"{self.name}: Master board not found in slot attributes"
265
- )
313
+ raise ConfigError(f"{self.name}: Master board not found in slot attributes")
266
314
 
267
315
  master = self.hw_module.slot.master
268
316
  master_key = self.hw_module.slot.slot_type.name
269
317
 
270
- i2c_masters = self._master_i2c_get_config(master)
318
+ i2c_masters = self.__master_i2c_get_config(master)
271
319
 
272
320
  if i2c_masters.get(master_key) is not None:
273
321
  dir = sysfs_root() / Path(i2c_masters[master_key])
274
322
  bus_dirs = list(dir.glob("i2c-*"))
323
+
275
324
  if len(bus_dirs) == 1:
276
325
  return bus_dirs[0]
277
326
  elif len(bus_dirs) > 1:
278
- raise ConfigError(f"Multiple master i2c buses found for {master_key}")
279
- raise ConfigError(f"No master i2c bus found for {master_key}")
327
+ raise ConfigError(f"Multiple master I2C buses found for {master_key}")
328
+
329
+ raise ConfigError(f"No master I2C bus found for {master_key}")
280
330
  else:
281
- raise ConfigError(f"Master i2c bus not found for {master_key}")
331
+ raise ConfigError(f"Master I2C bus not found for {master_key}")
282
332
 
283
333
  def get_i2c_bus_number(self) -> int:
284
334
  """
@@ -295,11 +345,14 @@ class Device(SysTree):
295
345
  parent_path = self.parent.sysfs_device.path
296
346
  else:
297
347
  parent_path = self.sysfs_device.path.parent
348
+
298
349
  if parent_path.is_symlink():
299
350
  parent_path = parent_path.readlink()
351
+
300
352
  bus_number = parent_path.name.split("-")[1]
353
+
301
354
  return int(bus_number)
302
355
 
303
356
  def __repr__(self) -> str:
304
357
  sysfs_path = getattr(self.sysfs_device, "path", "")
305
- return f"{self.name}; sysfs_path: {sysfs_path}"
358
+ return f"{self.name}; Path: {sysfs_path}"
ekfsm/devices/gpio.py CHANGED
@@ -1,19 +1,19 @@
1
- from abc import ABC, abstractmethod
2
1
  import os
3
2
  import re
3
+ from abc import ABC, abstractmethod
4
4
  from pathlib import Path
5
5
 
6
6
  import gpiod
7
- from gpiod.chip import LineSettings
8
7
  from gpiod.line import Direction, Value
8
+ from gpiod.line_settings import LineSettings
9
9
  from more_itertools import first_true
10
10
 
11
11
  from ekfsm.core.components import SysTree
12
12
  from ekfsm.exceptions import GPIOError
13
13
  from ekfsm.log import ekfsm_logger
14
14
 
15
- from .generic import Device
16
15
  from ..core.probe import ProbeableDevice
16
+ from .generic import Device
17
17
 
18
18
  gpio_pat = re.compile(r"gpiochip\d+")
19
19
 
@@ -24,7 +24,13 @@ def get_gpio_major_minor(path: Path) -> tuple[int, int]:
24
24
  dev = d / "dev"
25
25
  if dev.exists():
26
26
  content = dev.read_text().strip()
27
- major, minor = map(int, content.split(":"))
27
+ try:
28
+ major, minor = map(int, content.split(":"))
29
+ except Exception as e:
30
+ raise GPIOError(
31
+ GPIOError.ErrorType.NO_MAJOR_MINOR,
32
+ f"No minor/major number found for GPIO device at {path}",
33
+ ) from e
28
34
  return major, minor
29
35
 
30
36
  raise GPIOError(
@@ -51,11 +57,12 @@ def find_gpio_dev_with_major_minor(major: int, minor: int) -> Path | None:
51
57
 
52
58
 
53
59
  class GPIO(Device):
54
-
55
60
  def __init__(
56
61
  self,
57
62
  name: str,
58
63
  parent: SysTree | None = None,
64
+ children: Device | None = None,
65
+ abort: bool = False,
59
66
  *args,
60
67
  **kwargs,
61
68
  ):
@@ -63,6 +70,7 @@ class GPIO(Device):
63
70
  name,
64
71
  parent,
65
72
  None,
73
+ abort,
66
74
  *args,
67
75
  **kwargs,
68
76
  )
@@ -72,21 +80,15 @@ class GPIO(Device):
72
80
 
73
81
  assert self.gpio is not None
74
82
  match = re.search(r"\d+", self.gpio.name)
75
- if match:
76
- self.number: int = int(match.group().strip())
77
- else:
78
- raise GPIOError(
79
- GPIOError.ErrorType.NO_MATCHING_DEVICE, "Failed to find matching device"
80
- )
83
+
84
+ if not match:
85
+ raise GPIOError(GPIOError.ErrorType.NO_MATCHING_DEVICE, "Failed to find matching device")
86
+
87
+ self.number: int = int(match.group().strip())
81
88
 
82
89
  self.init_dev()
83
90
 
84
- def _find_gpio_dev(
85
- self,
86
- parent: SysTree | None = None,
87
- *args,
88
- **kwargs,
89
- ) -> tuple[int, int]:
91
+ def _find_gpio_dev(self, parent: SysTree | None = None, *args, **kwargs) -> tuple[int, int]:
90
92
  self.addr = self.get_i2c_chip_addr()
91
93
  self.logger.debug(f"GPIO: {self.addr}")
92
94
  self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
@@ -97,9 +99,13 @@ class GPIO(Device):
97
99
  try:
98
100
  self.dev = gpiod.Chip(str(self.gpio))
99
101
  self.initialized = True
102
+ return
100
103
  except FileNotFoundError:
104
+ self.initialized = False
101
105
  raise FileNotFoundError(f"{self.gpio} does not exist")
102
106
 
107
+ self.initialized = False
108
+
103
109
  def num_lines(self) -> int:
104
110
  """
105
111
  Get number of GPIO lines available on the device.
@@ -153,9 +159,7 @@ class GPIO(Device):
153
159
  default=None,
154
160
  )
155
161
  ) is not None:
156
- raise GPIOError(
157
- GPIOError.ErrorType.INVALID_PIN, f"GPIO {invalid_pin} is invalid."
158
- )
162
+ raise GPIOError(GPIOError.ErrorType.INVALID_PIN, f"GPIO {invalid_pin} is invalid.")
159
163
 
160
164
  def set_direction(self, pin: int, direction: bool) -> None:
161
165
  """
@@ -187,10 +191,12 @@ class GPIOExpander(GPIO):
187
191
  self,
188
192
  name: str,
189
193
  parent: SysTree | None,
194
+ children: Device | None = None,
195
+ abort: bool = False,
190
196
  *args,
191
197
  **kwargs,
192
198
  ):
193
- super().__init__(name, parent, None, *args, **kwargs)
199
+ super().__init__(name, parent, None, abort, *args, **kwargs)
194
200
 
195
201
  def __str__(self) -> str:
196
202
  return (
@@ -204,15 +210,17 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
204
210
  self,
205
211
  name: str,
206
212
  parent: SysTree | None,
213
+ children: Device | None = None,
214
+ abort: bool = False,
207
215
  *args,
208
216
  **kwargs,
209
217
  ):
210
- super().__init__(name, parent, None, *args, **kwargs)
218
+ super().__init__(name, parent, None, abort, *args, **kwargs)
211
219
 
212
220
  def probe(self, *args, **kwargs) -> bool:
213
- from ekfsm.core import HwModule
221
+ from ekfsm.core import HWModule
214
222
 
215
- assert isinstance(self.hw_module, HwModule)
223
+ assert isinstance(self.hw_module, HWModule)
216
224
  id, _ = self.read_board_id_rev()
217
225
  self.logger.debug(f"Probing EKFIdentificationIOExpander: {id}")
218
226
 
@@ -245,7 +253,7 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
245
253
  )
246
254
 
247
255
  def revision(self) -> str:
248
- id, rev = self.read_board_id_rev()
256
+ _, rev = self.read_board_id_rev()
249
257
  return str(rev)
250
258
 
251
259
  def read_id_gpio_inputs(self) -> int:
ekfsm/devices/iio.py CHANGED
@@ -1,7 +1,9 @@
1
- from .generic import SysFSDevice
1
+ from ekfsm.exceptions import ConversionError, SysFSError
2
2
 
3
+ from .generic import SysfsDevice
3
4
 
4
- def iio_get_in_value(dev: SysFSDevice, attrset: str) -> float:
5
+
6
+ def iio_get_in_value(dev: SysfsDevice, attrset: str) -> float:
5
7
  """
6
8
  Calculate a value from an IIO in_* attribute set, using the _raw, _scale and _offset attributes (if present).
7
9
  Typical name of attrset are "in_temp" or "in_voltage0".
@@ -26,33 +28,15 @@ def iio_get_in_value(dev: SysFSDevice, attrset: str) -> float:
26
28
  if the neiter _input nor _raw attribute is found
27
29
 
28
30
  """
29
- # check if _input exists
30
- try:
31
- content = dev.read_attr_utf8(f"{attrset}_input")
32
- return float(content)
33
- except StopIteration:
34
- pass
35
-
36
- # check if _raw exists
37
31
  try:
38
- content = dev.read_attr_utf8(f"{attrset}_raw")
39
- except StopIteration:
40
- raise FileNotFoundError(f"{attrset}_raw not found")
41
-
42
- raw = float(content)
43
-
44
- # use offset if present
45
- try:
46
- content = dev.read_attr_utf8(f"{attrset}_offset")
47
- raw += float(content)
48
- except StopIteration:
49
- pass
50
-
51
- # use scale if present
52
- try:
53
- content = dev.read_attr_utf8(f"{attrset}_scale")
54
- raw *= float(content)
55
- except StopIteration:
56
- pass
57
-
58
- return raw
32
+ content = dev.read_float(f"{attrset}_input")
33
+ except (SysFSError, ConversionError):
34
+ try:
35
+ raw = dev.read_float(f"{attrset}_raw")
36
+ offset = dev.read_float(f"{attrset}_offset")
37
+ scale = dev.read_float(f"{attrset}_scale")
38
+ content = (raw + offset) * scale
39
+ except (SysFSError, ConversionError) as e:
40
+ raise FileNotFoundError from e
41
+
42
+ return content
@@ -1,10 +1,8 @@
1
- from pathlib import Path
2
-
3
1
  from ekfsm.core.components import SysTree
4
2
  from ekfsm.log import ekfsm_logger
3
+ from ekfsm.utils import next_or_raise
5
4
 
6
- from ..core.sysfs import SysFSDevice
7
-
5
+ from ..core.sysfs import list_sysfs_attributes
8
6
  from .generic import Device
9
7
  from .iio import iio_get_in_value
10
8
 
@@ -28,22 +26,23 @@ class IIOThermalHumidity(Device):
28
26
  name: str,
29
27
  parent: SysTree | None = None,
30
28
  children: list[Device] | None = None,
29
+ abort: bool = False,
31
30
  *args,
32
31
  **kwargs,
33
32
  ):
34
33
  self.logger = ekfsm_logger("IIOThermalHumidity:" + name)
35
- super().__init__(name, parent, children, *args, **kwargs)
34
+ super().__init__(name, parent, children, abort, *args, **kwargs)
36
35
  self.addr = self.get_i2c_chip_addr()
37
- self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
36
+ self.sysfs_device = self.get_i2c_sysfs_device(self.addr, False)
37
+
38
+ # TODO: We can just search the attributes directly
39
+ iio_dir = self.sysfs.path.glob("iio:device*")
40
+ attrs = next_or_raise(iio_dir, FileNotFoundError("IIO entry not found"))
38
41
 
39
- dir = list(Path(self.sysfs_device.path).glob("iio:device*"))
40
- if len(dir) == 0:
41
- raise FileNotFoundError("iio entry not found")
42
- self.iio_sysfs = SysFSDevice(dir[0])
43
- self.logger.debug(f"iio: {self.iio_sysfs.path}")
42
+ self.sysfs_device.extend_attributes(list_sysfs_attributes(attrs))
44
43
 
45
44
  def temperature(self) -> float:
46
- return iio_get_in_value(self.iio_sysfs, "in_temp") / 1000.0
45
+ return iio_get_in_value(self.sysfs, "in_temp") / 1000.0
47
46
 
48
47
  def humidity(self) -> float:
49
- return iio_get_in_value(self.iio_sysfs, "in_humidityrelative") / 1000.0
48
+ return iio_get_in_value(self.sysfs, "in_humidityrelative") / 1000.0