ekfsm 1.4.0a43__py3-none-any.whl → 1.4.0a60__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.

@@ -42,7 +42,7 @@ children:
42
42
  # - write: write_customer_area
43
43
  # - read: customer_area
44
44
  - device_type: IO4Edge
45
- name: "SMC"
45
+ name: "I4E"
46
46
  provides:
47
47
  management:
48
48
  - identify_firmware
@@ -85,3 +85,10 @@ children:
85
85
  - kick
86
86
  - device_type: ThermalHumidity
87
87
  name: "th"
88
+ - device_type: ButtonArray
89
+ name: "buttons"
90
+ service_suffix: "gpios"
91
+ children:
92
+ - device_type: Button
93
+ name: "eject"
94
+ channel_id: 2
@@ -0,0 +1,19 @@
1
+ from contextlib import contextmanager
2
+
3
+
4
+ class Connectable:
5
+
6
+ def __init__(self, client=None):
7
+ self._client = client
8
+ self._connected = False
9
+
10
+ @contextmanager
11
+ def connect(self):
12
+ if not self._connected:
13
+ client = self._client(self.service_addr, command_timeout=self.timeout)
14
+ self._connected = True
15
+ try:
16
+ yield client
17
+ finally:
18
+ client.close()
19
+ del client
ekfsm/core/sysfs.py CHANGED
@@ -166,7 +166,7 @@ class SysfsDevice(MutableMapping):
166
166
 
167
167
  def read_int(self, attr) -> int:
168
168
  """
169
- Read a sysfs attribute as an integer
169
+ Read a sysfs attribute stored as a string as an integer
170
170
 
171
171
  Parameters
172
172
  ----------
@@ -186,7 +186,7 @@ class SysfsDevice(MutableMapping):
186
186
  """
187
187
  try:
188
188
  value = self.read_attr_utf8(attr).strip()
189
- return int(value, 16)
189
+ return int(value)
190
190
  except StopIteration as e:
191
191
  raise SysFSError(f"'{attr}' sysfs attribute does not exist") from e
192
192
  except SysFSError:
@@ -194,6 +194,36 @@ class SysfsDevice(MutableMapping):
194
194
  except ValueError as e:
195
195
  raise ConversionError("Failed to convert sysfs value to int") from e
196
196
 
197
+ def read_hex(self, attr) -> int:
198
+ """
199
+ Read a sysfs attribute stored as a hexadecimal integer as integer
200
+
201
+ Parameters
202
+ ----------
203
+ attr: str
204
+ The sysfs attribute to read
205
+
206
+ Returns
207
+ -------
208
+ The sysfs attribute as a hexadecimal integer
209
+
210
+ Raises
211
+ ------
212
+ SysFSError
213
+ If the sysfs attribute does not exist
214
+ ConversionError
215
+ If the sysfs attribute could not be converted to a hexadecimal integer
216
+ """
217
+ try:
218
+ value = self.read_attr_utf8(attr).strip()
219
+ return int(value, 16)
220
+ except StopIteration as e:
221
+ raise SysFSError(f"'{attr}' sysfs attribute does not exist") from e
222
+ except SysFSError:
223
+ raise
224
+ except ValueError as e:
225
+ raise ConversionError("Failed to convert sysfs value to hex int") from e
226
+
197
227
  def read_utf8(self, attr, strip=True) -> str:
198
228
  """
199
229
  Read a sysfs attribute as a UTF-8 encoded string
ekfsm/devices/button.py CHANGED
@@ -1,5 +1,8 @@
1
1
  from typing import Callable
2
2
  from ekfsm.devices.generic import Device
3
+ from ekfsm.log import ekfsm_logger
4
+
5
+ logger = ekfsm_logger(__name__)
3
6
 
4
7
 
5
8
  class Button(Device):
@@ -17,12 +20,15 @@ class Button(Device):
17
20
  *args,
18
21
  **kwargs,
19
22
  ):
23
+ logger.debug(f"Initializing Button '{name}' on channel {channel_id}")
20
24
 
21
25
  super().__init__(name, parent, children, abort, *args, **kwargs)
22
26
 
23
27
  self.channel_id = channel_id
28
+ logger.debug(f"Button '{name}' assigned to channel {channel_id}")
24
29
 
25
30
  self._handler: Callable | None = None
31
+ logger.info(f"Button '{name}' initialized on channel {channel_id}")
26
32
 
27
33
  @property
28
34
  def handler(self):
@@ -43,8 +49,17 @@ class Button(Device):
43
49
  """
44
50
  if callable(func):
45
51
  self._handler = func
52
+ logger.info(
53
+ f"Handler set for button '{self.name}' on channel {self.channel_id}"
54
+ )
55
+ logger.debug(
56
+ f"Handler function: {func.__name__ if hasattr(func, '__name__') else str(func)}"
57
+ )
46
58
  else:
47
59
  self._handler = None
60
+ logger.debug(
61
+ f"Handler cleared for button '{self.name}' on channel {self.channel_id}"
62
+ )
48
63
 
49
64
  def __repr__(self):
50
65
  return f"{self.name}; Channel ID: {self.channel_id}"
@@ -2,9 +2,12 @@ import threading
2
2
  from ekfsm.devices.button import Button
3
3
  from ekfsm.devices.generic import Device
4
4
  from ekfsm.devices.io4edge import IO4Edge
5
- import io4edge_client.binaryiotypeb as binio
5
+ from ekfsm.log import ekfsm_logger
6
+ from io4edge_client.binaryiotypeb import Client, Pb
6
7
  import io4edge_client.functionblock as fb
7
8
 
9
+ logger = ekfsm_logger(__name__)
10
+
8
11
 
9
12
  class ButtonArray(Device):
10
13
  """
@@ -28,31 +31,58 @@ class ButtonArray(Device):
28
31
  *args,
29
32
  **kwargs,
30
33
  ):
34
+ logger.debug(
35
+ f"Initializing ButtonArray '{name}' with parent device {parent.deviceId}"
36
+ )
31
37
 
32
- super().__init__(name, parent, children, abort, *args, **kwargs)
38
+ Device.__init__(self, name, parent, children, abort, *args, **kwargs)
33
39
 
34
40
  self.name = name
35
41
 
36
42
  if service_suffix is not None:
37
43
  self.service_suffix = service_suffix
44
+ logger.debug(f"Using custom service suffix: {service_suffix}")
38
45
  else:
39
46
  self.service_suffix = name
47
+ logger.debug(f"Using default service suffix: {name}")
40
48
 
41
49
  self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
50
+ self.timeout = keepaliveInterval / 1000 + 5
42
51
 
43
- self.client = binio.Client(
44
- self.service_addr, command_timeout=keepaliveInterval / 1000 + 5
52
+ logger.info(
53
+ f"ButtonArray '{name}' configured with service address: {self.service_addr}"
45
54
  )
55
+ logger.debug(
56
+ f"Keepalive interval: {keepaliveInterval}ms, timeout: {self.timeout}s"
57
+ )
58
+
59
+ try:
60
+ self.client = Client(
61
+ self.service_addr, command_timeout=self.timeout, connect=False
62
+ )
63
+ logger.debug(f"IO4Edge client created for service: {self.service_addr}")
64
+ except Exception as e:
65
+ logger.error(
66
+ f"Failed to create IO4Edge client for {self.service_addr}: {e}"
67
+ )
68
+ raise
46
69
 
47
- self.subscriptionType = binio.Pb.SubscriptionType.BINARYIOTYPEB_ON_RISING_EDGE
70
+ self.subscriptionType = Pb.SubscriptionType.BINARYIOTYPEB_ON_RISING_EDGE
48
71
  self.stream_cfg = fb.Pb.StreamControlStart(
49
72
  bucketSamples=1, # 1 sample per bucket, also ein event pro bucket
50
73
  keepaliveInterval=keepaliveInterval,
51
74
  bufferedSamples=2, # 2 samples werden gepuffert
52
75
  low_latency_mode=True, # schickt soweit moeglich sofort die Events
53
76
  )
77
+ logger.debug(
78
+ "Stream configuration initialized with rising edge subscription and low latency mode"
79
+ )
54
80
 
55
- def read(self, stop_event: threading.Event | None = None, timeout: float = 0.1):
81
+ # Log button children count
82
+ button_count = sum(1 for child in (children or []) if isinstance(child, Button))
83
+ logger.info(f"ButtonArray '{name}' initialized with {button_count} button(s)")
84
+
85
+ def read(self, stop_event: threading.Event | None = None, timeout: float = 1):
56
86
  """
57
87
  Read all button events and dispatch to handlers.
58
88
 
@@ -67,38 +97,110 @@ class ButtonArray(Device):
67
97
  ----
68
98
  This method blocks and should be run in a separate thread.
69
99
  """
70
- self.client.start_stream(
71
- binio.Pb.StreamControlStart(
72
- subscribeChannel=tuple(
73
- binio.Pb.SubscribeChannel(
100
+ button_channels = [
101
+ button for button in self.children if isinstance(button, Button)
102
+ ]
103
+
104
+ if not button_channels:
105
+ logger.warning(
106
+ f"No button children found in ButtonArray '{self.name}', read operation will have no effect"
107
+ )
108
+ return
109
+
110
+ logger.info(
111
+ f"Starting button event reading for {len(button_channels)} buttons on '{self.name}'"
112
+ )
113
+ logger.debug(
114
+ f"Read timeout: {timeout}s, stop_event provided: {stop_event is not None}"
115
+ )
116
+
117
+ try:
118
+ with self.client as client:
119
+ logger.debug(
120
+ f"IO4Edge client connected to service: {self.service_addr}"
121
+ )
122
+
123
+ # Prepare subscription channels
124
+ subscribe_channels = tuple(
125
+ Pb.SubscribeChannel(
74
126
  channel=button.channel_id,
75
127
  subscriptionType=self.subscriptionType,
76
128
  )
77
- for button in self.children
78
- if isinstance(button, Button)
129
+ for button in button_channels
79
130
  )
80
- ),
81
- self.stream_cfg,
82
- )
83
- try:
84
- while not (stop_event and stop_event.is_set()):
131
+
132
+ channel_ids = [button.channel_id for button in button_channels]
133
+ logger.debug(
134
+ f"Subscribing to {len(subscribe_channels)} button channels: {channel_ids}"
135
+ )
136
+
137
+ client.start_stream(
138
+ Pb.StreamControlStart(subscribeChannel=subscribe_channels),
139
+ self.stream_cfg,
140
+ )
141
+ logger.info(
142
+ f"Button event stream started for ButtonArray '{self.name}'"
143
+ )
144
+
145
+ event_count = 0
85
146
  try:
86
- _, samples = self.client.read_stream(timeout=timeout)
87
- for sample in samples.samples:
88
- for button in self.children:
89
- if isinstance(button, Button):
90
- pressed = bool(sample.inputs & (1 << button.channel_id))
91
- if pressed and button.handler:
92
- button.handler()
93
- except TimeoutError:
94
- continue
95
- finally:
96
- try:
97
- if stop_event:
98
- stop_event.clear()
99
- self.client.stop_stream()
100
- except TimeoutError:
101
- pass
147
+ while not (stop_event and stop_event.is_set()):
148
+ try:
149
+ _, samples = client.read_stream(timeout=timeout)
150
+
151
+ for sample in samples.samples:
152
+ for button in button_channels:
153
+ pressed = bool(
154
+ sample.inputs & (1 << button.channel_id)
155
+ )
156
+ if pressed:
157
+ event_count += 1
158
+ button_name = getattr(button, "name", "unnamed")
159
+ logger.debug(
160
+ f"Button press on channel {button.channel_id} ({button_name})"
161
+ )
162
+
163
+ if button.handler:
164
+ try:
165
+ logger.debug(
166
+ f"Calling handler for button on channel {button.channel_id}"
167
+ )
168
+ button.handler()
169
+ except Exception as e:
170
+ logger.error(
171
+ f"Error in button handler for channel {button.channel_id}: {e}"
172
+ )
173
+ else:
174
+ logger.debug(
175
+ f"No handler set for button on channel {button.channel_id}"
176
+ )
177
+
178
+ except TimeoutError:
179
+ # Timeout is expected during normal operation
180
+ continue
181
+ except Exception as e:
182
+ logger.error(
183
+ f"Error reading button events from stream: {e}"
184
+ )
185
+ break
186
+
187
+ except KeyboardInterrupt:
188
+ logger.info(
189
+ f"Button reading interrupted for ButtonArray '{self.name}'"
190
+ )
191
+ finally:
192
+ logger.info(
193
+ f"Button event reading stopped for '{self.name}' after processing {event_count} events"
194
+ )
195
+ if stop_event:
196
+ stop_event.clear()
197
+ logger.debug("Stop event cleared")
198
+
199
+ except Exception as e:
200
+ logger.error(
201
+ f"Failed to establish connection or start stream for ButtonArray '{self.name}': {e}"
202
+ )
203
+ raise
102
204
 
103
205
  def __repr__(self):
104
206
  return f"{self.name}; Service Address: {self.service_addr}"
ekfsm/devices/colorLed.py CHANGED
@@ -1,7 +1,10 @@
1
1
  from ekfsm.devices.generic import Device
2
2
  from ekfsm.devices.ledArray import LEDArray
3
+ from ekfsm.log import ekfsm_logger
3
4
  from io4edge_client.api.colorLED.python.colorLED.v1alpha1.colorLED_pb2 import Color
4
5
 
6
+ logger = ekfsm_logger(__name__)
7
+
5
8
 
6
9
  class ColorLED(Device):
7
10
  """
@@ -18,6 +21,7 @@ class ColorLED(Device):
18
21
  *args,
19
22
  **kwargs,
20
23
  ):
24
+ logger.debug(f"Initializing ColorLED '{name}' on channel {channel_id}")
21
25
 
22
26
  super().__init__(name, parent, children, abort, *args, **kwargs)
23
27
 
@@ -25,6 +29,9 @@ class ColorLED(Device):
25
29
  self.channel_id = channel_id
26
30
 
27
31
  self.client = parent.client
32
+ logger.info(
33
+ f"ColorLED '{name}' initialized on channel {channel_id} with parent LEDArray"
34
+ )
28
35
 
29
36
  def describe(self):
30
37
  pass
@@ -44,7 +51,19 @@ class ColorLED(Device):
44
51
  TimeoutError
45
52
  if the command times out
46
53
  """
47
- return self.client.get(self.channel_id)
54
+ logger.debug(
55
+ f"Getting color LED state for '{self.name}' on channel {self.channel_id}"
56
+ )
57
+ try:
58
+ result = self.client.get(self.channel_id)
59
+ color, blink = result
60
+ logger.debug(f"ColorLED '{self.name}' state: color={color}, blink={blink}")
61
+ return result
62
+ except Exception as e:
63
+ logger.error(
64
+ f"Failed to get ColorLED '{self.name}' state on channel {self.channel_id}: {e}"
65
+ )
66
+ raise
48
67
 
49
68
  def set(self, color: Color, blink: bool) -> None:
50
69
  """
@@ -64,7 +83,19 @@ class ColorLED(Device):
64
83
  TimeoutError
65
84
  if the command times out
66
85
  """
67
- self.client.set(self.channel_id, color, blink)
86
+ logger.info(
87
+ f"Setting ColorLED '{self.name}' on channel {self.channel_id}: color={color}, blink={blink}"
88
+ )
89
+ try:
90
+ self.client.set(self.channel_id, color, blink)
91
+ logger.debug(
92
+ f"ColorLED '{self.name}' successfully set to color={color}, blink={blink}"
93
+ )
94
+ except Exception as e:
95
+ logger.error(
96
+ f"Failed to set ColorLED '{self.name}' on channel {self.channel_id}: {e}"
97
+ )
98
+ raise
68
99
 
69
100
  def __repr__(self):
70
101
  return f"{self.name}; Channel ID: {self.channel_id}"
ekfsm/devices/coretemp.py CHANGED
@@ -7,6 +7,9 @@ from pathlib import Path
7
7
  import ekfsm.core
8
8
  from ekfsm.core.sysfs import sysfs_root
9
9
  from ekfsm.devices.generic import Device
10
+ from ekfsm.log import ekfsm_logger
11
+
12
+ logger = ekfsm_logger(__name__)
10
13
 
11
14
  # Path to the root of the HWMON sysfs filesystem
12
15
  HWMON_ROOT = sysfs_root() / Path("class/hwmon")
@@ -55,8 +58,16 @@ class CoreTemp(Device):
55
58
  *args,
56
59
  **kwargs,
57
60
  ):
58
- dir = find_core_temp_dir(sysfs_root() / Path("class/hwmon"))
59
- self.sysfs_device = ekfsm.core.sysfs.SysfsDevice(dir, False)
61
+ logger.debug(f"Initializing CoreTemp device '{name}'")
62
+
63
+ try:
64
+ dir = find_core_temp_dir(sysfs_root() / Path("class/hwmon"))
65
+ logger.debug(f"Found coretemp directory: {dir}")
66
+ self.sysfs_device = ekfsm.core.sysfs.SysfsDevice(dir, False)
67
+ logger.info(f"CoreTemp '{name}' initialized with sysfs device at {dir}")
68
+ except FileNotFoundError as e:
69
+ logger.error(f"Failed to initialize CoreTemp '{name}': {e}")
70
+ raise
60
71
 
61
72
  super().__init__(name, parent, None, abort, *args, **kwargs)
62
73
 
@@ -69,4 +80,16 @@ class CoreTemp(Device):
69
80
  int
70
81
  The CPU temperature in degrees Celsius.
71
82
  """
72
- return self.sysfs.read_int("temp1_input") / 1000
83
+ logger.debug(f"Reading CPU temperature for CoreTemp '{self.name}'")
84
+ try:
85
+ temp_raw = self.sysfs.read_float("temp1_input")
86
+ temp_celsius = temp_raw / 1000
87
+ logger.debug(
88
+ f"CoreTemp '{self.name}' raw reading: {temp_raw}, temperature: {temp_celsius}°C"
89
+ )
90
+ return temp_celsius
91
+ except Exception as e:
92
+ logger.error(
93
+ f"Failed to read CPU temperature for CoreTemp '{self.name}': {e}"
94
+ )
95
+ raise
ekfsm/devices/eeprom.py CHANGED
@@ -520,8 +520,8 @@ class EKF_EEPROM(Validatable_EEPROM, ProbeableDevice):
520
520
  The date the device was manufactured.
521
521
  """
522
522
  area = self._content[self._date_mft_index_start : self._date_mft_index_end]
523
- encoded_mft_date = area[::-1]
524
- return self._decode_date(encoded_mft_date)
523
+ # encoded_mft_date = area[::-1]
524
+ return self._decode_date(area)
525
525
 
526
526
  @validated
527
527
  def repaired_at(self) -> date:
@@ -533,8 +533,8 @@ class EKF_EEPROM(Validatable_EEPROM, ProbeableDevice):
533
533
  The most recent date the device was repaired.
534
534
  """
535
535
  area = self._content[self._date_rep_index_start : self._date_rep_index_end]
536
- encoded_rep_date = area[::-1]
537
- return self._decode_date(encoded_rep_date)
536
+ # encoded_rep_date = area[::-1]
537
+ return self._decode_date(area)
538
538
 
539
539
  @validated
540
540
  def write_repaired_at(self, date: date) -> None:
ekfsm/devices/generic.py CHANGED
@@ -63,7 +63,9 @@ class Device(SysTree):
63
63
  try:
64
64
  func = list(interface.values())[0]
65
65
  except IndexError:
66
- raise ConfigError(f"{self.name}: No function given for interface {name}.")
66
+ raise ConfigError(
67
+ f"{self.name}: No function given for interface {name}."
68
+ )
67
69
 
68
70
  if not hasattr(self, func):
69
71
  if abort:
@@ -114,10 +116,14 @@ class Device(SysTree):
114
116
  return self.sysfs.read_float(attr)
115
117
  case "int":
116
118
  return self.sysfs.read_int(attr)
119
+ case "hex":
120
+ return self.sysfs.read_hex(attr)
117
121
  case _:
118
122
  raise UnsupportedModeError(f"Mode {mode} is not supported")
119
123
 
120
- def read_attr_or_default(self, attr: str, mode: str = "utf", strip: bool = True, default=None):
124
+ def read_attr_or_default(
125
+ self, attr: str, mode: str = "utf", strip: bool = True, default=None
126
+ ):
121
127
  try:
122
128
  return self.read_attr(attr, mode, strip)
123
129
  except UnsupportedModeError:
@@ -149,7 +155,11 @@ class Device(SysTree):
149
155
  None:
150
156
  If the sysfs device is not set or the attribute does not exist.
151
157
  """
152
- if self.sysfs_device is not None and len(attr) != 0 and attr in [x.name for x in self.sysfs_device.attributes]:
158
+ if (
159
+ self.sysfs_device is not None
160
+ and len(attr) != 0
161
+ and attr in [x.name for x in self.sysfs_device.attributes]
162
+ ):
153
163
  return self.sysfs_device.read_attr_bytes(attr)
154
164
  return None
155
165
 
@@ -222,7 +232,9 @@ class Device(SysTree):
222
232
 
223
233
  chip_addr = self.device_args.get("addr")
224
234
  if chip_addr is None:
225
- raise ConfigError(f"{self.name}: Chip address not provided in board definition")
235
+ raise ConfigError(
236
+ f"{self.name}: Chip address not provided in board definition"
237
+ )
226
238
 
227
239
  if not hasattr(self.parent, "sysfs_device") or self.parent.sysfs_device is None:
228
240
  # our device is the top level device of the slot
@@ -230,12 +242,16 @@ class Device(SysTree):
230
242
  slot_attributes = self.hw_module.slot.attributes
231
243
 
232
244
  if slot_attributes is None:
233
- raise ConfigError(f"{self.name}: Slot attributes not provided in system configuration")
245
+ raise ConfigError(
246
+ f"{self.name}: Slot attributes not provided in system configuration"
247
+ )
234
248
 
235
249
  if not self.hw_module.is_master:
236
250
  # slot coding is only used for non-master devices
237
251
  if not hasattr(slot_attributes, "slot_coding"):
238
- raise ConfigError(f"{self.name}: Slot coding not provided in slot attributes")
252
+ raise ConfigError(
253
+ f"{self.name}: Slot coding not provided in slot attributes"
254
+ )
239
255
 
240
256
  slot_coding_mask = 0xFF
241
257
 
@@ -246,7 +262,9 @@ class Device(SysTree):
246
262
 
247
263
  return chip_addr
248
264
 
249
- def get_i2c_sysfs_device(self, addr: int, driver_required=True, find_driver: Callable | None = None) -> SysfsDevice:
265
+ def get_i2c_sysfs_device(
266
+ self, addr: int, driver_required=True, find_driver: Callable | None = None
267
+ ) -> SysfsDevice:
250
268
  from ekfsm.core.components import HWModule
251
269
 
252
270
  parent = self.parent
@@ -275,8 +293,12 @@ class Device(SysTree):
275
293
  # regular I2C devices that follow the `${I2C_BUS}-${ADDR}` pattern. To address this issue, we
276
294
  # initialize the ACPI _STR object for each PRP device with the necessary information, which is
277
295
  # 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()
296
+ if (entry / "firmware_node").exists() and (
297
+ entry / "firmware_node" / "description"
298
+ ).exists():
299
+ description = (
300
+ (entry / "firmware_node/description").read_text().strip()
301
+ )
280
302
  acpi_addr = int(description.split(" - ")[0], 16)
281
303
 
282
304
  if acpi_addr == addr:
@@ -289,11 +311,16 @@ class Device(SysTree):
289
311
  if acpi_addr == addr:
290
312
  return SysfsDevice(entry, driver_required, find_driver)
291
313
 
292
- raise FileNotFoundError(f"Device with address 0x{addr:x} not found in {i2c_bus_path}")
314
+ raise FileNotFoundError(
315
+ f"Device with address 0x{addr:x} not found in {i2c_bus_path}"
316
+ )
293
317
 
294
318
  @staticmethod
295
319
  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:
320
+ if (
321
+ master.config.get("bus_masters") is not None
322
+ and master.config["bus_masters"].get("i2c") is not None
323
+ ):
297
324
  return master.config["bus_masters"]["i2c"]
298
325
  else:
299
326
  raise ConfigError("Master definition incomplete")
@@ -310,7 +337,9 @@ class Device(SysTree):
310
337
  else:
311
338
  # another board is the master
312
339
  if self.hw_module.slot.master is None:
313
- raise ConfigError(f"{self.name}: Master board not found in slot attributes")
340
+ raise ConfigError(
341
+ f"{self.name}: Master board not found in slot attributes"
342
+ )
314
343
 
315
344
  master = self.hw_module.slot.master
316
345
  master_key = self.hw_module.slot.slot_type.name
ekfsm/devices/io4edge.py CHANGED
@@ -1,10 +1,13 @@
1
1
  from typing import Callable, Optional
2
2
  from ekfsm.core.components import HWModule
3
3
  from ekfsm.devices.generic import Device
4
+ from ekfsm.log import ekfsm_logger
4
5
  import io4edge_client.core.coreclient as Client
5
6
 
6
7
  from re import sub
7
8
 
9
+ logger = ekfsm_logger(__name__)
10
+
8
11
 
9
12
  class IO4Edge(Device):
10
13
  """
@@ -20,6 +23,7 @@ class IO4Edge(Device):
20
23
  *args,
21
24
  **kwargs,
22
25
  ):
26
+ logger.debug(f"Initializing IO4Edge device '{name}'")
23
27
 
24
28
  super().__init__(name, parent, children, abort, *args, **kwargs)
25
29
 
@@ -29,16 +33,27 @@ class IO4Edge(Device):
29
33
  or not hasattr(attr, "slot_coding")
30
34
  or getattr(attr, "slot_coding") is None
31
35
  ):
36
+ logger.error(
37
+ f"Slot attributes for {self.hw_module.slot.name} are not set or do not contain 'slot_coding'"
38
+ )
32
39
  raise ValueError(
33
40
  f"Slot attributes for {self.hw_module.slot.name} are not set or do not contain 'slot_coding'."
34
41
  )
35
42
  else:
36
43
  geoaddr = int(attr.slot_coding)
37
44
  self._geoaddr = geoaddr
45
+ logger.debug(f"IO4Edge '{name}' geo address: {geoaddr}")
38
46
 
39
47
  _, module_name = sub(r"-.*$", "", self.hw_module.board_type).split(maxsplit=1)
40
48
  self._module_name = module_name
41
- self.client = Client.new_core_client(self.deviceId)
49
+ logger.debug(f"IO4Edge '{name}' module name: {module_name}")
50
+
51
+ try:
52
+ self.client = Client.new_core_client(self.deviceId)
53
+ logger.info(f"IO4Edge '{name}' initialized with device ID: {self.deviceId}")
54
+ except Exception as e:
55
+ logger.error(f"Failed to create IO4Edge core client for '{name}': {e}")
56
+ raise
42
57
 
43
58
  @property
44
59
  def deviceId(self) -> str:
@@ -49,9 +64,10 @@ class IO4Edge(Device):
49
64
  return f"{self._module_name}-geo_addr{self._geoaddr:02d}"
50
65
 
51
66
  def identify_firmware(self) -> tuple[str, str]:
67
+ response = self.client.identify_firmware()
52
68
  return (
53
- self.client.identify_firmware().title,
54
- self.client.identify_firmware().version,
69
+ response.title,
70
+ response.version,
55
71
  )
56
72
 
57
73
  def load_firmware(
ekfsm/devices/ledArray.py CHANGED
@@ -1,7 +1,10 @@
1
1
  from ekfsm.devices.generic import Device
2
2
  from ekfsm.devices.io4edge import IO4Edge
3
+ from ekfsm.log import ekfsm_logger
3
4
  from io4edge_client.colorLED import Client
4
5
 
6
+ logger = ekfsm_logger(__name__)
7
+
5
8
 
6
9
  class LEDArray(Device):
7
10
  """
@@ -18,6 +21,9 @@ class LEDArray(Device):
18
21
  *args,
19
22
  **kwargs,
20
23
  ):
24
+ logger.debug(
25
+ f"Initializing LEDArray '{name}' with parent device {parent.deviceId}"
26
+ )
21
27
 
22
28
  super().__init__(name, parent, children, abort, *args, **kwargs)
23
29
 
@@ -25,12 +31,24 @@ class LEDArray(Device):
25
31
 
26
32
  if service_suffix is not None:
27
33
  self.service_suffix = service_suffix
34
+ logger.debug(f"Using custom service suffix: {service_suffix}")
28
35
  else:
29
36
  self.service_suffix = name
37
+ logger.debug(f"Using default service suffix: {name}")
30
38
 
31
39
  self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
32
-
33
- self.client = Client(self.service_addr)
40
+ logger.info(
41
+ f"LEDArray '{name}' configured with service address: {self.service_addr}"
42
+ )
43
+
44
+ try:
45
+ self.client = Client(self.service_addr)
46
+ logger.debug(f"LEDArray client created for service: {self.service_addr}")
47
+ except Exception as e:
48
+ logger.error(
49
+ f"Failed to create LEDArray client for {self.service_addr}: {e}"
50
+ )
51
+ raise
34
52
 
35
53
  def __repr__(self):
36
54
  return f"{self.name}; Service Address: {self.service_addr}"
@@ -1,8 +1,11 @@
1
1
  from ekfsm.devices.generic import Device
2
2
  from ekfsm.devices.io4edge import IO4Edge
3
+ from ekfsm.log import ekfsm_logger
3
4
  from io4edge_client.pixelDisplay import Client
4
5
  from PIL import Image
5
6
 
7
+ logger = ekfsm_logger(__name__)
8
+
6
9
 
7
10
  class PixelDisplay(Device):
8
11
  """
@@ -19,6 +22,9 @@ class PixelDisplay(Device):
19
22
  *args,
20
23
  **kwargs,
21
24
  ):
25
+ logger.debug(
26
+ f"Initializing PixelDisplay '{name}' with parent device {parent.deviceId}"
27
+ )
22
28
 
23
29
  super().__init__(name, parent, children, abort, *args, **kwargs)
24
30
 
@@ -26,24 +32,46 @@ class PixelDisplay(Device):
26
32
 
27
33
  if service_suffix is not None:
28
34
  self.service_suffix = service_suffix
35
+ logger.debug(f"Using custom service suffix: {service_suffix}")
29
36
  else:
30
37
  self.service_suffix = name
38
+ logger.debug(f"Using default service suffix: {name}")
31
39
 
32
40
  self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
41
+ logger.info(
42
+ f"PixelDisplay '{name}' configured with service address: {self.service_addr}"
43
+ )
33
44
 
34
- self.client = Client(self.service_addr)
45
+ try:
46
+ self.client = Client(self.service_addr, connect=False)
47
+ logger.debug(
48
+ f"PixelDisplay client created for service: {self.service_addr}"
49
+ )
50
+ except Exception as e:
51
+ logger.error(
52
+ f"Failed to create PixelDisplay client for {self.service_addr}: {e}"
53
+ )
54
+ raise
35
55
 
36
56
  def describe(self) -> dict:
37
57
  """
38
58
  Returns a description of the pixel display.
39
59
  """
40
- describe = self.client.describe()
41
- desc = {
42
- "height": describe.height_pixel,
43
- "width": describe.width_pixel,
44
- "max_num_of_pixel": describe.max_num_of_pixel,
45
- }
46
- return desc
60
+ logger.debug(f"Getting PixelDisplay description for '{self.name}'")
61
+ try:
62
+ describe = self.client.describe()
63
+ desc = {
64
+ "height": describe.height_pixel,
65
+ "width": describe.width_pixel,
66
+ "max_num_of_pixel": describe.max_num_of_pixel,
67
+ }
68
+ logger.debug(f"PixelDisplay '{self.name}' description: {desc}")
69
+ return desc
70
+ except Exception as e:
71
+ logger.error(
72
+ f"Failed to get PixelDisplay description for '{self.name}': {e}"
73
+ )
74
+ raise
47
75
 
48
76
  @property
49
77
  def height(self) -> int:
@@ -65,7 +93,13 @@ class PixelDisplay(Device):
65
93
  @raises RuntimeError: if the command fails
66
94
  @raises TimeoutError: if the command times out
67
95
  """
68
- self.client.set_display_off()
96
+ logger.info(f"Turning off PixelDisplay '{self.name}'")
97
+ try:
98
+ self.client.set_display_off()
99
+ logger.debug(f"PixelDisplay '{self.name}' successfully turned off")
100
+ except Exception as e:
101
+ logger.error(f"Failed to turn off PixelDisplay '{self.name}': {e}")
102
+ raise
69
103
 
70
104
  def display_image(self, path: str) -> None:
71
105
  """
@@ -73,16 +107,31 @@ class PixelDisplay(Device):
73
107
  @raises RuntimeError: if the command fails
74
108
  @raises TimeoutError: if the command times out
75
109
  """
76
- with Image.open(path) as img:
77
- img = img.convert("RGB")
78
- pix = img.load()
79
-
80
- for i in range(0, 320, 16):
81
- pix_area = []
82
- for k in range(0, 16):
83
- for j in range(0, 240):
84
- pix_area.append(pix[j, i + k])
85
- self.client.set_pixel_area(0, i, 239, pix_area)
110
+ logger.info(f"Displaying image '{path}' on PixelDisplay '{self.name}'")
111
+ try:
112
+ with Image.open(path) as img:
113
+ img = img.convert("RGB")
114
+ pix = img.load()
115
+ logger.debug(
116
+ f"Image '{path}' loaded and converted to RGB for PixelDisplay '{self.name}'"
117
+ )
118
+
119
+ with self.client as client:
120
+ logger.debug(f"Sending pixel data to PixelDisplay '{self.name}'")
121
+ for i in range(0, 320, 16):
122
+ pix_area = []
123
+ for k in range(0, 16):
124
+ for j in range(0, 240):
125
+ pix_area.append(pix[j, i + k])
126
+ client.set_pixel_area(0, i, 239, pix_area)
127
+ logger.debug(
128
+ f"Image successfully displayed on PixelDisplay '{self.name}'"
129
+ )
130
+ except Exception as e:
131
+ logger.error(
132
+ f"Failed to display image '{path}' on PixelDisplay '{self.name}': {e}"
133
+ )
134
+ raise
86
135
 
87
136
  def __repr__(self):
88
137
  return f"{self.name}; Service Address: {self.service_addr}"
ekfsm/devices/pmbus.py CHANGED
@@ -52,7 +52,9 @@ def retry(max_attempts=5, delay=0.5):
52
52
  except Exception as e:
53
53
  attempts += 1
54
54
  if attempts == max_attempts:
55
- logger.exception(f"Failed to execute {func.__name__} after {max_attempts} attempts: {e}")
55
+ logger.exception(
56
+ f"Failed to execute {func.__name__} after {max_attempts} attempts: {e}"
57
+ )
56
58
  raise e
57
59
  logger.info(f"Retrying execution of {func.__name__} in {delay}s...")
58
60
  sleep(delay)
@@ -121,7 +123,9 @@ class PMBus(Device, ProbeableDevice):
121
123
  attrs = list_sysfs_attributes(entry)
122
124
  self.sysfs_device.extend_attributes(attrs)
123
125
 
124
- debug_attrs_path = sysfs_root().joinpath(f"kernel/debug/pmbus/{entry.name}")
126
+ debug_attrs_path = sysfs_root().joinpath(
127
+ f"kernel/debug/pmbus/{entry.name}"
128
+ )
125
129
  debug_attrs = list_sysfs_attributes(debug_attrs_path)
126
130
  self.sysfs_device.extend_attributes(debug_attrs)
127
131
  except FileNotFoundError:
@@ -194,7 +198,7 @@ class PMBus(Device, ProbeableDevice):
194
198
  -------
195
199
  PSU status as defined in PSUStatus
196
200
  """
197
- status = self.sysfs.read_int("status0_input")
201
+ status = self.sysfs.read_hex("status0_input")
198
202
  return PSUStatus(status)
199
203
 
200
204
  @retry()
@@ -206,7 +210,7 @@ class PMBus(Device, ProbeableDevice):
206
210
  -------
207
211
  PSU status as defined in PSUStatus
208
212
  """
209
- status = self.sysfs.read_int("status1_input")
213
+ status = self.sysfs.read_hex("status1_input")
210
214
  return PSUStatus(status)
211
215
 
212
216
  # Temperature Interface
ekfsm/devices/smbios.py CHANGED
@@ -2,9 +2,12 @@ from pathlib import Path
2
2
 
3
3
  from ekfsm.core.components import HWModule
4
4
  from ekfsm.core.sysfs import SysfsDevice, sysfs_root
5
+ from ekfsm.log import ekfsm_logger
5
6
 
6
7
  from .generic import Device
7
8
 
9
+ logger = ekfsm_logger(__name__)
10
+
8
11
 
9
12
  class SMBIOS(Device):
10
13
  """
@@ -26,7 +29,15 @@ class SMBIOS(Device):
26
29
  *args,
27
30
  **kwargs,
28
31
  ):
29
- self.sysfs_device: SysfsDevice | None = SysfsDevice(sysfs_root() / Path("devices/virtual/dmi/id"), False)
32
+ logger.debug(f"Initializing SMBIOS device '{name}'")
33
+
34
+ try:
35
+ dmi_path = sysfs_root() / Path("devices/virtual/dmi/id")
36
+ self.sysfs_device: SysfsDevice | None = SysfsDevice(dmi_path, False)
37
+ logger.info(f"SMBIOS '{name}' initialized with DMI table at {dmi_path}")
38
+ except Exception as e:
39
+ logger.error(f"Failed to initialize SMBIOS '{name}' with DMI table: {e}")
40
+ raise
30
41
 
31
42
  super().__init__(name, parent, None, abort, *args, **kwargs)
32
43
 
@@ -39,4 +50,11 @@ class SMBIOS(Device):
39
50
  str
40
51
  The board revision.
41
52
  """
42
- return self.sysfs.read_utf8("board_version")
53
+ logger.debug(f"Reading board revision for SMBIOS '{self.name}'")
54
+ try:
55
+ revision = self.sysfs.read_utf8("board_version")
56
+ logger.debug(f"SMBIOS '{self.name}' board revision: {revision}")
57
+ return revision
58
+ except Exception as e:
59
+ logger.error(f"Failed to read board revision for SMBIOS '{self.name}': {e}")
60
+ raise
@@ -1,6 +1,9 @@
1
1
  from ekfsm.devices.generic import Device
2
2
  from ekfsm.devices.io4edge import IO4Edge
3
- from io4edge_client.analogintypea import Client
3
+ from ekfsm.log import ekfsm_logger
4
+ from io4edge_client.analogintypeb import Client
5
+
6
+ logger = ekfsm_logger(__name__)
4
7
 
5
8
 
6
9
  class ThermalHumidity(Device):
@@ -18,6 +21,9 @@ class ThermalHumidity(Device):
18
21
  *args,
19
22
  **kwargs,
20
23
  ):
24
+ logger.debug(
25
+ f"Initializing ThermalHumidity sensor '{name}' with parent device {parent.deviceId}"
26
+ )
21
27
 
22
28
  super().__init__(name, parent, children, abort, *args, **kwargs)
23
29
 
@@ -25,12 +31,26 @@ class ThermalHumidity(Device):
25
31
 
26
32
  if service_suffix is not None:
27
33
  self.service_suffix = service_suffix
34
+ logger.debug(f"Using custom service suffix: {service_suffix}")
28
35
  else:
29
36
  self.service_suffix = name
37
+ logger.debug(f"Using default service suffix: {name}")
30
38
 
31
39
  self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
40
+ logger.info(
41
+ f"ThermalHumidity '{name}' configured with service address: {self.service_addr}"
42
+ )
32
43
 
33
- self.client = Client(self.service_addr)
44
+ try:
45
+ self.client = Client(self.service_addr, connect=False)
46
+ logger.debug(
47
+ f"ThermalHumidity client created for service: {self.service_addr}"
48
+ )
49
+ except Exception as e:
50
+ logger.error(
51
+ f"Failed to create ThermalHumidity client for {self.service_addr}: {e}"
52
+ )
53
+ raise
34
54
 
35
55
  def temperature(self) -> float:
36
56
  """
@@ -43,7 +63,16 @@ class ThermalHumidity(Device):
43
63
  TimeoutError
44
64
  if the command times out
45
65
  """
46
- return self.client.value()
66
+ logger.debug(f"Reading temperature from ThermalHumidity sensor '{self.name}'")
67
+ try:
68
+ temp = self.client.value()
69
+ logger.debug(f"ThermalHumidity '{self.name}' temperature: {temp}°C")
70
+ return temp
71
+ except Exception as e:
72
+ logger.error(
73
+ f"Failed to read temperature from ThermalHumidity '{self.name}': {e}"
74
+ )
75
+ raise
47
76
 
48
77
  def __repr__(self):
49
78
  return f"{self.name}; Service Address: {self.service_addr}"
ekfsm/devices/watchdog.py CHANGED
@@ -1,7 +1,10 @@
1
1
  from ekfsm.devices.generic import Device
2
2
  from ekfsm.devices.io4edge import IO4Edge
3
+ from ekfsm.log import ekfsm_logger
3
4
  from io4edge_client.watchdog import Client
4
5
 
6
+ logger = ekfsm_logger(__name__)
7
+
5
8
 
6
9
  class Watchdog(Device):
7
10
  """
@@ -18,6 +21,9 @@ class Watchdog(Device):
18
21
  *args,
19
22
  **kwargs,
20
23
  ):
24
+ logger.debug(
25
+ f"Initializing Watchdog '{name}' with parent device {parent.deviceId}"
26
+ )
21
27
 
22
28
  super().__init__(name, parent, children, abort, *args, **kwargs)
23
29
 
@@ -25,12 +31,24 @@ class Watchdog(Device):
25
31
 
26
32
  if service_suffix is not None:
27
33
  self.service_suffix = service_suffix
34
+ logger.debug(f"Using custom service suffix: {service_suffix}")
28
35
  else:
29
36
  self.service_suffix = name
37
+ logger.debug(f"Using default service suffix: {name}")
30
38
 
31
39
  self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
40
+ logger.info(
41
+ f"Watchdog '{name}' configured with service address: {self.service_addr}"
42
+ )
32
43
 
33
- self.client = Client(self.service_addr)
44
+ try:
45
+ self.client = Client(self.service_addr, connect=False)
46
+ logger.debug(f"Watchdog client created for service: {self.service_addr}")
47
+ except Exception as e:
48
+ logger.error(
49
+ f"Failed to create Watchdog client for {self.service_addr}: {e}"
50
+ )
51
+ raise
34
52
 
35
53
  def describe(self):
36
54
  pass
@@ -46,7 +64,13 @@ class Watchdog(Device):
46
64
  TimeoutError
47
65
  if the command times out
48
66
  """
49
- self.client.kick()
67
+ logger.debug(f"Kicking watchdog '{self.name}' on service {self.service_addr}")
68
+ try:
69
+ self.client.kick()
70
+ logger.debug(f"Watchdog '{self.name}' kick successful")
71
+ except Exception as e:
72
+ logger.error(f"Failed to kick watchdog '{self.name}': {e}")
73
+ raise
50
74
 
51
75
  def __repr__(self):
52
76
  return f"{self.name}; Service Address: {self.service_addr}"
@@ -1,20 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ekfsm
3
- Version: 1.4.0a43
3
+ Version: 1.4.0a60
4
4
  Summary: The EKF System Management Library (ekfsm) is a sensor monitoring suite for Compact PCI Serial devices.
5
5
  Author-email: Jan Jansen <jan@ekf.de>, Klaus Popp <klaus.popp@ci4rail.com>, Felix Päßler <fp@ekf.de>
6
- Requires-Python: >=3.10
6
+ Requires-Python: >=3.12
7
7
  Requires-Dist: anytree
8
8
  Requires-Dist: click>=8.0.1
9
9
  Requires-Dist: crcmod
10
10
  Requires-Dist: gpiod>=2.1.0
11
11
  Requires-Dist: hexdump
12
- Requires-Dist: io4edge-client>=1.6.2
12
+ Requires-Dist: io4edge-client>=2.0.3
13
13
  Requires-Dist: ipdb>=0.13.13
14
14
  Requires-Dist: more-itertools
15
15
  Requires-Dist: munch
16
16
  Requires-Dist: pillow
17
- Requires-Dist: protobuf
17
+ Requires-Dist: protobuf>=6.32.1
18
18
  Requires-Dist: schema>=0.7.7
19
19
  Requires-Dist: smbus2
20
20
  Requires-Dist: termcolor>=3.0.1
@@ -18,38 +18,39 @@ ekfsm/boards/oem/ekf/sq1-track.yaml,sha256=YU83BQjGu-4ejirwnGxd38sJme859kdRovkZy
18
18
  ekfsm/boards/oem/ekf/sq3-quartet.yaml,sha256=JaHJn_lph2iwLz6cwXu0bjRMgj33OXt1-VBNGFu0U3s,1330
19
19
  ekfsm/boards/oem/ekf/srf-fan.yaml,sha256=Mcu1Q8B1Ih10hoc_hbkGlppBmbOFcufsVUR42iW4Rwc,1368
20
20
  ekfsm/boards/oem/ekf/sur-uart.yaml,sha256=VaNP2BSlNTi1lDco16Ma9smPEAMaVKvx-ZNDcm3QptM,1890
21
- ekfsm/boards/oem/ekf/z1010.yaml,sha256=Z0m0AaRnBFL3QYvSnzHVauXm3laN4lrVv6ewccyY6Hk,2369
21
+ ekfsm/boards/oem/ekf/z1010.yaml,sha256=oz1BI2dGlk3vn0g58-QGC1k35-V0Svh4ESpwHlneJBs,2560
22
22
  ekfsm/boards/oem/hitron/hdrc-300s.yaml,sha256=SwBHTWVqENBun_iZattPbvA9koc-LOI_MIXjLahKyyI,548
23
23
  ekfsm/core/__init__.py,sha256=xfuU072JY_--561hfSS8ToBM-wgswY4Z-YwDMZkVA6c,348
24
24
  ekfsm/core/components.py,sha256=iTetqtzNjEEj6FDJNfvXxW_cNNRlLX5_WgB5z3g3zqg,3883
25
+ ekfsm/core/connections.py,sha256=ffvqohwFxUnJTFeHG_O7SmXtNGOZqqTZ9cOfGvHC2P8,458
25
26
  ekfsm/core/probe.py,sha256=DgJvkvMjVk09n0Rzn13ybRvidrmFn_D2PD56XS-KgxU,262
26
27
  ekfsm/core/slots.py,sha256=qmxfssVitsMFZYbJRCpOOsvEGqh8JVYdRS-_5BliOF4,6580
27
- ekfsm/core/sysfs.py,sha256=CWH1BXlanHAFzK6KY23qLuFvBZ7Q9td3X6JLRwb3vu0,7309
28
+ ekfsm/core/sysfs.py,sha256=1UJ19ZISCnQ4Zz3fwZmad7dle85mhNugF4beTVdapQE,8228
28
29
  ekfsm/core/utils.py,sha256=AjVj_QCb9umUmZZsGFPyCISdOKYlhRRSOTVjPu_gxyA,4678
29
30
  ekfsm/devices/__init__.py,sha256=EDFzBuUHt_3_6JD4cageHWZfoMn3WUIIDZk4yEOWgfg,1451
30
- ekfsm/devices/button.py,sha256=z3vzpTCY4ZweUd1M9_eJvepM7A-FchapdsaMz5RCUPE,1207
31
- ekfsm/devices/buttonArray.py,sha256=KOI7ycLY9RxXOpalCxJz4rOqSC7vp6dihIGkvl2BdXw,3603
32
- ekfsm/devices/colorLed.py,sha256=H0SFl0SkMHIV5Is31XQJfisDu2nmPh0TuWrtlhsi1bI,1622
33
- ekfsm/devices/coretemp.py,sha256=UEsPTMYm05qNCnqnPtbAoYdIfvVGkDl9rJg3tkAP748,1992
34
- ekfsm/devices/eeprom.py,sha256=cwcaM2YCKrw6du8d4zT_M3e4oFJh3XNQc5cQM2vqERE,33090
31
+ ekfsm/devices/button.py,sha256=JZr9v8_z12GlVBQ-4b08AY40cqy4mErvZtxt_q4pbr8,1893
32
+ ekfsm/devices/buttonArray.py,sha256=YwRRUY-7f2QEc5bE_qXWg4zulupn7mrcpco3pARf9N0,8141
33
+ ekfsm/devices/colorLed.py,sha256=i6RXR8QZq3ukGPd3PszAHH_p5_JesDbm2EaLKIM-Yls,2824
34
+ ekfsm/devices/coretemp.py,sha256=rurB6eq7yIdwupusla4DzAWOz4i4Xe1RgOoeuMlaJX8,2897
35
+ ekfsm/devices/eeprom.py,sha256=p-ACUn1f9xH-Hjx4oLPmXGRHLbNf94q99EPLfWtV-2M,33070
35
36
  ekfsm/devices/ekf_ccu_uc.py,sha256=hjs5r7TKsrFhyyGKClgSFBpchQpFcE7fTZeuLrYiflY,15905
36
37
  ekfsm/devices/ekf_sur_led.py,sha256=qpCn7qj4v27QjxB5xTC5wIXKfefpveOd3FPK3JElyVk,1997
37
- ekfsm/devices/generic.py,sha256=LboZ7eanyC2xUnSX3-S7VCA87aTbJenlGtKsUk5JS2Q,12858
38
+ ekfsm/devices/generic.py,sha256=4fwwnUyToQ7bvQ7U3tZ7Huynu7ruf2Fog_KAqkO3yN0,13365
38
39
  ekfsm/devices/gpio.py,sha256=37P7uu-ncg0TWf0Xs1iZM-PrX7F_09asd4IhWj6z21Y,10058
39
40
  ekfsm/devices/iio.py,sha256=1Jyh5wJ53DxvpWNBqYW07kkaAN9446ebWGCMk2UIuWM,1265
40
41
  ekfsm/devices/iio_thermal_humidity.py,sha256=KCeEqBlczroxp-hjsiRlXQdmmnJR9Ck3d3ztths5Vvc,1547
41
42
  ekfsm/devices/imu.py,sha256=GwJrnC-WztpE5oGnkIp-laFkSp3_gbSp9YpJ8jBsUOs,423
42
- ekfsm/devices/io4edge.py,sha256=hk_EzcXVR0-sD2zn6pMuO2txYNnTDKmMaaXS2N__8E4,2743
43
- ekfsm/devices/ledArray.py,sha256=6DCHs1nJz12aHQNPytaM_KIasQYdB29ZN7GzPb_EcJ4,902
43
+ ekfsm/devices/io4edge.py,sha256=PBjWygDWSUpB0prmYyjUi2cBxQsDyeeqiw3ykfIrdmQ,3406
44
+ ekfsm/devices/ledArray.py,sha256=NdZ3zQpApL___-r_bv5YPBQsabo5qQxvXil2pfOZv6Y,1616
44
45
  ekfsm/devices/mux.py,sha256=QpVWsD43Mx1mtCDG_nOxtHeIeU2Cigc62UljJ_utSXQ,1974
45
- ekfsm/devices/pixelDisplay.py,sha256=68SANN4S15_NncuDvu0hyjjdqhpxYn14cmxjXwfGqgU,2425
46
- ekfsm/devices/pmbus.py,sha256=fi_kR7TlkcqYhHuB7PA2PjANCYmo3MSPeV-B5ZI_WrQ,7004
47
- ekfsm/devices/smbios.py,sha256=4P-sxOW27pkzIfVlvFNyVJOcZlYgk2KCTCEUvKf5D94,1067
46
+ ekfsm/devices/pixelDisplay.py,sha256=Td-MhNYzJVcDrFzjTcnyrOYpXn2oz5w9YOttJqHZMD4,4573
47
+ ekfsm/devices/pmbus.py,sha256=-3p-D0w4smzRCeqZWCure8I2Lx4y310kG1U0Rmr890c,7104
48
+ ekfsm/devices/smbios.py,sha256=rH60ixqSGv_b5TgloJjH_kwZShefFnlCebQDpdQZx7M,1803
48
49
  ekfsm/devices/smbus.py,sha256=kbtjEnfrVI6lbfqAbojVqO5nnnpa6HanLf0X2iZjgCg,500
49
- ekfsm/devices/thermal_humidity.py,sha256=HnoiR3mZxwuE74KTFiRhG4Znq21vgcyMFJizSCnULzw,1207
50
+ ekfsm/devices/thermal_humidity.py,sha256=t848kJArG0VAEX5nOnRKomdFdx4wxFflBGMsg_wgZKw,2386
50
51
  ekfsm/devices/utils.py,sha256=qeDNpTpQ3bLGYERAH5lMsifmOaEcQ32FOwEt8tvmjWU,174
51
- ekfsm/devices/watchdog.py,sha256=QjzvXrXV_XRvCYiy-sr6LOMJywb84aNThuDpsWGvxZk,1202
52
- ekfsm-1.4.0a43.dist-info/METADATA,sha256=VntlMZhuL2I0H21H89ri0KHMJqk8s9KF7QDcl1zslnw,6717
53
- ekfsm-1.4.0a43.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
54
- ekfsm-1.4.0a43.dist-info/entry_points.txt,sha256=WhUR4FzuxPoGrbTOKLsNJO7NAnk2qd4T30fqzN1yLw8,45
55
- ekfsm-1.4.0a43.dist-info/RECORD,,
52
+ ekfsm/devices/watchdog.py,sha256=SEsF9dztlLfUSvD5r9yMWegXtsefwGXY3W1oJE63OMs,2224
53
+ ekfsm-1.4.0a60.dist-info/METADATA,sha256=f2WxM9_C4gbDC_aYv2q5y8iHf8zmlqUg-H8Uz7E1hWw,6725
54
+ ekfsm-1.4.0a60.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
+ ekfsm-1.4.0a60.dist-info/entry_points.txt,sha256=WhUR4FzuxPoGrbTOKLsNJO7NAnk2qd4T30fqzN1yLw8,45
56
+ ekfsm-1.4.0a60.dist-info/RECORD,,