ekfsm 1.2.0a7__py3-none-any.whl → 1.3.0a32__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/mux.py CHANGED
@@ -1,8 +1,11 @@
1
1
  from ekfsm.core.components import SysTree
2
+ from ekfsm.log import ekfsm_logger
2
3
 
3
- from ..core.sysfs import SysFSDevice
4
+ from ..core.sysfs import SysfsDevice
4
5
  from .generic import Device
5
6
 
7
+ logger = ekfsm_logger(__name__)
8
+
6
9
 
7
10
  class MuxChannel(Device):
8
11
  """
@@ -25,20 +28,21 @@ class MuxChannel(Device):
25
28
  def __init__(
26
29
  self,
27
30
  name: str,
28
- channel_id: int,
29
31
  parent: "I2CMux",
30
32
  children: list[Device] | None = None,
33
+ abort=False,
34
+ channel_id: int | None = None,
31
35
  *args,
32
36
  **kwargs,
33
37
  ) -> None:
34
- super().__init__(name=name, parent=parent, children=children)
38
+ super().__init__(name, parent, children, abort, *args, **kwargs)
35
39
  self.channel_id = channel_id
36
40
 
37
41
  assert parent.sysfs_device is not None
38
42
  assert isinstance(self.parent, I2CMux)
39
43
 
40
44
  path = parent.sysfs_device.path / f"channel-{self.channel_id}"
41
- self.sysfs_device = SysFSDevice(path)
45
+ self.sysfs_device = SysfsDevice(path, False)
42
46
 
43
47
 
44
48
  class I2CMux(Device):
@@ -59,11 +63,12 @@ class I2CMux(Device):
59
63
  self,
60
64
  name: str,
61
65
  parent: SysTree | None = None,
62
- children: list[MuxChannel] | None = None,
66
+ children: list[Device] | None = None,
67
+ abort=False,
63
68
  *args,
64
69
  **kwargs,
65
70
  ):
66
- super().__init__(name, parent, children, **kwargs)
71
+ super().__init__(name, parent, children, abort, *args, **kwargs)
67
72
 
68
73
  self.addr = self.get_i2c_chip_addr()
69
74
  self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
ekfsm/devices/pmbus.py CHANGED
@@ -1,20 +1,17 @@
1
+ import re
1
2
  from enum import IntFlag
2
- from pathlib import Path
3
+ from functools import wraps
4
+ from time import sleep
3
5
 
4
6
  from ekfsm.core.components import SysTree
7
+ from ekfsm.exceptions import HWMonError
8
+ from ekfsm.log import ekfsm_logger
5
9
 
6
- from ..core.sysfs import SysFSDevice, sysfs_root
7
-
8
- from .generic import Device
9
10
  from ..core.probe import ProbeableDevice
11
+ from ..core.sysfs import list_sysfs_attributes, sysfs_root
12
+ from .generic import Device
10
13
 
11
- from time import sleep
12
- from functools import wraps
13
- from ekfsm.log import ekfsm_logger
14
- from threading import Lock
15
- import re
16
-
17
- __all__ = ["PsuStatus", "PmBus", "retry"]
14
+ __all__ = ["PSUStatus", "PMBus", "retry"]
18
15
 
19
16
  logger = ekfsm_logger(__name__)
20
17
 
@@ -34,8 +31,8 @@ def retry(max_attempts=5, delay=0.5):
34
31
 
35
32
  Important
36
33
  ---------
37
- This decorator is thread-safe, meaning a read attempt is atomic and cannot
38
- be interupted by scheduler.
34
+ This decorator is _not_ thread-safe across multiple ekfsm processes. Unfortunately,
35
+ we cannot use fcntl or flock syscalls with files on virtual filesystems like sysfs.
39
36
 
40
37
  Parameters
41
38
  ----------
@@ -45,26 +42,19 @@ def retry(max_attempts=5, delay=0.5):
45
42
  The delay in seconds between attempts.
46
43
  """
47
44
 
48
- lock = Lock()
49
-
50
45
  def decorator(func):
51
46
  @wraps(func)
52
47
  def wrapper(*args, **kwargs):
53
48
  attempts = 0
54
49
  while attempts < max_attempts:
55
- with lock:
56
- try:
57
- return func(*args, **kwargs)
58
- except Exception as e:
59
- attempts += 1
60
- if attempts == max_attempts:
61
- logger.exception(
62
- f"Failed to execute {func.__name__} after {max_attempts} attempts: {e}"
63
- )
64
- raise e
65
- logger.info(
66
- f"Retrying execution of {func.__name__} in {delay}s..."
67
- )
50
+ try:
51
+ return func(*args, **kwargs)
52
+ except Exception as e:
53
+ attempts += 1
54
+ if attempts == max_attempts:
55
+ logger.exception(f"Failed to execute {func.__name__} after {max_attempts} attempts: {e}")
56
+ raise e
57
+ logger.info(f"Retrying execution of {func.__name__} in {delay}s...")
68
58
  sleep(delay)
69
59
 
70
60
  return wrapper
@@ -72,7 +62,7 @@ def retry(max_attempts=5, delay=0.5):
72
62
  return decorator
73
63
 
74
64
 
75
- class PsuStatus(IntFlag):
65
+ class PSUStatus(IntFlag):
76
66
  """
77
67
  Represents the status of a PSU according to STATUS_BYTE register.
78
68
 
@@ -108,44 +98,47 @@ class PsuStatus(IntFlag):
108
98
  OK = 0x00
109
99
 
110
100
 
111
- class PmBus(Device, ProbeableDevice):
112
- """
113
- This class represents a PMBus device (e.g. a PSU).
114
- """
101
+ logger = ekfsm_logger(__name__)
102
+
115
103
 
104
+ class PMBus(Device, ProbeableDevice):
116
105
  def __init__(
117
106
  self,
118
107
  name: str,
119
108
  parent: SysTree | None = None,
120
109
  children: list[Device] | None = None,
110
+ abort: bool = False,
121
111
  *args,
122
112
  **kwargs,
123
113
  ):
124
- super().__init__(name, parent, children, *args, **kwargs)
114
+ super().__init__(name, parent, children, abort, *args, **kwargs)
125
115
  self.addr = self.get_i2c_chip_addr()
126
- self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
127
-
128
- files = list(Path(self.sysfs_device.path).rglob("hwmon/*/in1_input"))
129
- if len(files) == 0:
130
- raise FileNotFoundError("No HWMON entries found in sysfs")
131
- self.hwmon_sysfs = SysFSDevice(files[0].parent)
132
-
133
- self.debugfs_root = sysfs_root() / "kernel/debug/pmbus"
134
- files = list(self.debugfs_root.rglob("hwmon*/status*_input"))
135
- if len(files) == 0:
136
- raise FileNotFoundError("No HWMON entries found in debugfs")
137
- self.hwmon_debugfs = SysFSDevice(files[0].parent)
116
+ self.sysfs_device = self.get_i2c_sysfs_device(self.addr, driver_required=True)
117
+
118
+ try:
119
+ for entry in self.sysfs_device.path.glob("hwmon/hwmon*"):
120
+ if entry.is_dir():
121
+ attrs = list_sysfs_attributes(entry)
122
+ self.sysfs_device.extend_attributes(attrs)
123
+
124
+ debug_attrs_path = sysfs_root().joinpath(f"kernel/debug/pmbus/{entry.name}")
125
+ debug_attrs = list_sysfs_attributes(debug_attrs_path)
126
+ self.sysfs_device.extend_attributes(debug_attrs)
127
+ except FileNotFoundError:
128
+ logger.debug("Expected sysfs attribute not found")
129
+ except StopIteration:
130
+ raise HWMonError("Device is not managed by hwmon subsystem")
138
131
 
139
132
  def probe(self, *args, **kwargs) -> bool:
140
- from ekfsm.core import HwModule
133
+ from ekfsm.core import HWModule
141
134
 
142
- assert isinstance(self.hw_module, HwModule)
135
+ assert isinstance(self.hw_module, HWModule)
143
136
  # compare the regexp from the board yaml file with the model
144
137
  return re.match(self.hw_module.id, self.model()) is not None
145
138
 
146
139
  # Voltage and Current Interfaces
147
- def _conversion(self, in_file: str) -> float:
148
- return float(self.hwmon_sysfs.read_attr_utf8(in_file)) / 1000.0
140
+ def __convert_and_scale(self, attr: str) -> float:
141
+ return self.sysfs.read_float(attr) / 1000.0
149
142
 
150
143
  @retry()
151
144
  def in1_input(self) -> float:
@@ -156,7 +149,7 @@ class PmBus(Device, ProbeableDevice):
156
149
  -------
157
150
  Input voltage in volts
158
151
  """
159
- return self._conversion("in1_input")
152
+ return self.__convert_and_scale("in1_input")
160
153
 
161
154
  @retry()
162
155
  def in2_input(self) -> float:
@@ -167,7 +160,7 @@ class PmBus(Device, ProbeableDevice):
167
160
  -------
168
161
  Input voltage in volts
169
162
  """
170
- return self._conversion("in2_input")
163
+ return self.__convert_and_scale("in2_input")
171
164
 
172
165
  @retry()
173
166
  def curr1_input(self) -> float:
@@ -178,7 +171,7 @@ class PmBus(Device, ProbeableDevice):
178
171
  -------
179
172
  Input current in amperes
180
173
  """
181
- return self._conversion("curr1_input")
174
+ return self.__convert_and_scale("curr1_input")
182
175
 
183
176
  @retry()
184
177
  def curr2_input(self) -> float:
@@ -189,32 +182,32 @@ class PmBus(Device, ProbeableDevice):
189
182
  -------
190
183
  Input current in amperes
191
184
  """
192
- return self._conversion("curr2_input")
185
+ return self.__convert_and_scale("curr2_input")
193
186
 
194
187
  # Status Interface
195
188
  @retry()
196
- def status0_input(self) -> PsuStatus:
189
+ def status0_input(self) -> PSUStatus:
197
190
  """
198
191
  Get the status of PSU page 1.
199
192
 
200
193
  Returns
201
194
  -------
202
- PSU status as defined in PsuStatus
195
+ PSU status as defined in PSUStatus
203
196
  """
204
- status = int(self.hwmon_debugfs.read_attr_utf8("status0_input").strip(), 16)
205
- return PsuStatus(status)
197
+ status = self.sysfs.read_int("status0_input")
198
+ return PSUStatus(status)
206
199
 
207
200
  @retry()
208
- def status1_input(self) -> PsuStatus:
201
+ def status1_input(self) -> PSUStatus:
209
202
  """
210
203
  Get the status of PSU page 2.
211
204
 
212
205
  Returns
213
206
  -------
214
- PSU status as defined in PsuStatus
207
+ PSU status as defined in PSUStatus
215
208
  """
216
- status = int(self.hwmon_debugfs.read_attr_utf8("status1_input").strip(), 16)
217
- return PsuStatus(status)
209
+ status = self.sysfs.read_int("status1_input")
210
+ return PSUStatus(status)
218
211
 
219
212
  # Temperature Interface
220
213
  @retry()
@@ -226,7 +219,7 @@ class PmBus(Device, ProbeableDevice):
226
219
  -------
227
220
  PSU temperature in degrees celsius
228
221
  """
229
- return self._conversion("temp1_input")
222
+ return self.__convert_and_scale("temp1_input")
230
223
 
231
224
  # Inventory Interface
232
225
  def vendor(self) -> str:
@@ -237,7 +230,7 @@ class PmBus(Device, ProbeableDevice):
237
230
  -------
238
231
  PSU vendor
239
232
  """
240
- return self.hwmon_sysfs.read_attr_utf8("vendor").strip()
233
+ return self.sysfs.read_utf8("vendor")
241
234
 
242
235
  def model(self) -> str:
243
236
  """
@@ -247,7 +240,7 @@ class PmBus(Device, ProbeableDevice):
247
240
  -------
248
241
  PSU model
249
242
  """
250
- return self.hwmon_sysfs.read_attr_utf8("model").strip()
243
+ return self.sysfs.read_utf8("model")
251
244
 
252
245
  def serial(self) -> str:
253
246
  """
@@ -257,7 +250,7 @@ class PmBus(Device, ProbeableDevice):
257
250
  -------
258
251
  PSU serial number
259
252
  """
260
- return self.hwmon_sysfs.read_attr_utf8("serial").strip()
253
+ return self.sysfs.read_utf8("serial")
261
254
 
262
255
  def revision(self) -> str:
263
256
  """
@@ -267,4 +260,4 @@ class PmBus(Device, ProbeableDevice):
267
260
  -------
268
261
  PSU revision
269
262
  """
270
- return self.hwmon_sysfs.read_attr_utf8("revision").strip()
263
+ return self.sysfs.read_utf8("revision")
ekfsm/devices/smbios.py CHANGED
@@ -1,6 +1,8 @@
1
1
  from pathlib import Path
2
- from ekfsm.core.components import HwModule
3
- from ekfsm.core.sysfs import SysFSDevice, sysfs_root
2
+
3
+ from ekfsm.core.components import HWModule
4
+ from ekfsm.core.sysfs import SysfsDevice, sysfs_root
5
+
4
6
  from .generic import Device
5
7
 
6
8
 
@@ -18,15 +20,15 @@ class SMBIOS(Device):
18
20
  def __init__(
19
21
  self,
20
22
  name: str,
21
- parent: HwModule | None = None,
23
+ parent: HWModule | None = None,
24
+ children: list["Device"] | None = None,
25
+ abort: bool = False,
22
26
  *args,
23
27
  **kwargs,
24
28
  ):
25
- self.sysfs_device: SysFSDevice = SysFSDevice(
26
- sysfs_root() / Path("devices/virtual/dmi/id")
27
- )
29
+ self.sysfs_device: SysfsDevice | None = SysfsDevice(sysfs_root() / Path("devices/virtual/dmi/id"), False)
28
30
 
29
- super().__init__(name, parent, None, *args, **kwargs)
31
+ super().__init__(name, parent, None, abort, *args, **kwargs)
30
32
 
31
33
  def revision(self) -> str:
32
34
  """
@@ -37,4 +39,4 @@ class SMBIOS(Device):
37
39
  str
38
40
  The board revision.
39
41
  """
40
- return self.sysfs_device.read_attr_utf8("board_version").strip()
42
+ return self.sysfs.read_utf8("board_version")
ekfsm/devices/smbus.py CHANGED
@@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
2
2
  from typing import List
3
3
 
4
4
 
5
- class SimSmbus(ABC):
5
+ class SimSMBus(ABC):
6
6
  @abstractmethod
7
7
  def read_word_data(self, cmd: int) -> int:
8
8
  pass
ekfsm/devices/utils.py CHANGED
@@ -1,13 +1,4 @@
1
1
  from crcmod.predefined import Crc
2
- from typing import Sequence
3
-
4
-
5
- def compute_int_from_bytes(data: Sequence[int]) -> int:
6
- # Combine the bytes into a single integer
7
- result = 0
8
- for num in data:
9
- result = (result << 8) | num
10
- return result
11
2
 
12
3
 
13
4
  def get_crc16_xmodem(data: bytes) -> int:
ekfsm/exceptions.py CHANGED
@@ -3,6 +3,7 @@ from enum import Enum
3
3
 
4
4
  class EkfSmException(Exception):
5
5
  """Base class for all exceptions in the EKFSM Library"""
6
+
6
7
  pass
7
8
 
8
9
 
@@ -12,7 +13,7 @@ class ConfigError(EkfSmException):
12
13
  pass
13
14
 
14
15
 
15
- class SYSFSError(EkfSmException):
16
+ class SysFSError(EkfSmException):
16
17
  """Error while handling sysfs pseudo file system"""
17
18
 
18
19
  pass
@@ -31,9 +32,31 @@ class GPIOError(EkfSmException):
31
32
  def __init__(self, error_type: ErrorType, details: str | None = None):
32
33
  self.error_type = error_type
33
34
  self.details = details
34
- super().__init__(
35
- f"{error_type.value}: {details}" if details else error_type.value
36
- )
35
+ super().__init__(f"{error_type.value}: {details}" if details else error_type.value)
36
+
37
+
38
+ class DriverError(EkfSmException):
39
+ """No driver found for device"""
40
+
41
+ pass
42
+
43
+
44
+ class HWMonError(EkfSmException):
45
+ """No HwMon entry found for device"""
46
+
47
+ pass
48
+
49
+
50
+ class ConversionError(EkfSmException):
51
+ """Failed to convert"""
52
+
53
+ pass
54
+
55
+
56
+ class UnsupportedModeError(EkfSmException):
57
+ """Format not supported"""
58
+
59
+ pass
37
60
 
38
61
 
39
62
  class FirmwareNodeError(EkfSmException):
@@ -47,9 +70,7 @@ class DataCorruptionError(EkfSmException):
47
70
 
48
71
  def __init__(self, details: str | None = None):
49
72
  self.details = details
50
- super().__init__(
51
- f"Data corruption: {details}" if details else "Data corruption"
52
- )
73
+ super().__init__(f"Data corruption: {details}" if details else "Data corruption")
53
74
 
54
75
 
55
76
  class AcquisitionError(EkfSmException):
ekfsm/lock.py CHANGED
@@ -16,8 +16,8 @@ the lockfile root directory.
16
16
 
17
17
  import fcntl
18
18
  import os
19
- from pathlib import Path
20
19
  from contextlib import contextmanager
20
+ from pathlib import Path
21
21
 
22
22
  USE_LOCK = True
23
23
  LOCKFILE_ROOT = "/var/lock/ekfsm"
ekfsm/simctrl.py CHANGED
@@ -1,20 +1,20 @@
1
1
  import socket
2
2
  import struct
3
- from unittest.mock import patch
4
3
  from pathlib import Path
4
+ from typing import List
5
+ from unittest.mock import patch
5
6
 
6
- from ekfsm.devices.smbus import SimSmbus
7
- from ekfsm.devices.gpio import EKFIdSimGpio
8
- from ekfsm.devices.gpio import SimGpio
9
- from .core.sysfs import set_sysfs_root
10
- from .core.components import SysTree
7
+ from smbus2 import SMBus
11
8
 
9
+ from ekfsm.devices.gpio import EKFIdSimGpio, SimGpio
10
+ from ekfsm.devices.smbus import SimSMBus
11
+
12
+ from .core.components import SysTree
13
+ from .core.sysfs import set_sysfs_root
12
14
  from .devices import GPIO
13
- from typing import List
14
- from smbus2 import SMBus
15
15
 
16
16
  GPIO_SIM_MAPPING: dict[str, SimGpio] = {}
17
- SMBUS_SIM_MAPPING: dict[str, SimSmbus] = {}
17
+ SMBUS_SIM_MAPPING: dict[str, SimSMBus] = {}
18
18
 
19
19
 
20
20
  def register_gpio_sim(major: int, minor: int, sim_gpio: SimGpio) -> None:
@@ -31,14 +31,14 @@ def find_gpio_dev_with_major_minor(major: int, minor: int) -> SimGpio:
31
31
  return GPIO_SIM_MAPPING[name]
32
32
 
33
33
 
34
- def register_smbus_sim(bus_num: int, i2c_addr: int, sim_smbus: SimSmbus) -> None:
34
+ def register_smbus_sim(bus_num: int, i2c_addr: int, sim_smbus: SimSMBus) -> None:
35
35
  name = f"{bus_num}:{i2c_addr}"
36
36
  if name in SMBUS_SIM_MAPPING:
37
37
  raise ValueError(f"SMBUS_SIM_MAPPING already contains {name}")
38
38
  SMBUS_SIM_MAPPING[name] = sim_smbus
39
39
 
40
40
 
41
- def find_smbus_dev(bus_num: int, i2c_addr: int) -> SimSmbus:
41
+ def find_smbus_dev(bus_num: int, i2c_addr: int) -> SimSMBus:
42
42
  name = f"{bus_num}:{i2c_addr}"
43
43
 
44
44
  if name not in SMBUS_SIM_MAPPING:
@@ -46,7 +46,7 @@ def find_smbus_dev(bus_num: int, i2c_addr: int) -> SimSmbus:
46
46
  return SMBUS_SIM_MAPPING[name]
47
47
 
48
48
 
49
- class GpioSimulator(GPIO):
49
+ class GPIOSimulator(GPIO):
50
50
  def __init__(
51
51
  self,
52
52
  name: str,
@@ -80,7 +80,7 @@ class GpioSimulator(GPIO):
80
80
  return f"GPIO_SIM({self.name})"
81
81
 
82
82
 
83
- class SmbusSimulator:
83
+ class SMBusSimulator:
84
84
  def __init__(self, bus_num: int):
85
85
  self.bus_num = bus_num
86
86
 
@@ -100,73 +100,12 @@ class SmbusSimulator:
100
100
  find_smbus_dev(self.bus_num, i2c_addr).write_word_data(cmd, data)
101
101
 
102
102
 
103
- def enable_gpio_simulation():
104
- patched_methods = []
105
-
106
- patched_methods.append(
107
- patch.object(GPIO, "__init__", new_callable=lambda: GpioSimulator.__init__)
108
- )
109
- patched_methods.append(
110
- patch.object(GPIO, "num_lines", new_callable=lambda: GpioSimulator.num_lines)
111
- )
112
- patched_methods.append(
113
- patch.object(GPIO, "set_pin", new_callable=lambda: GpioSimulator.set_pin)
114
- )
115
- patched_methods.append(
116
- patch.object(GPIO, "get_pin", new_callable=lambda: GpioSimulator.get_pin)
117
- )
118
- patched_methods.append(
119
- patch.object(
120
- GPIO, "set_direction", new_callable=lambda: GpioSimulator.set_direction
121
- )
122
- )
123
- patched_methods.append(
124
- patch.object(GPIO, "__str__", new_callable=lambda: GpioSimulator.__str__)
125
- )
126
- for pm in patched_methods:
127
- pm.start()
128
-
129
-
130
- def enable_smbus_simulation():
131
- patched_methods = []
132
-
133
- patched_methods.append(
134
- patch.object(SMBus, "__init__", new_callable=lambda: SmbusSimulator.__init__)
135
- )
136
- patched_methods.append(
137
- patch.object(
138
- SMBus, "read_word_data", new_callable=lambda: SmbusSimulator.read_word_data
139
- )
140
- )
141
- patched_methods.append(
142
- patch.object(
143
- SMBus,
144
- "read_block_data",
145
- new_callable=lambda: SmbusSimulator.read_block_data,
146
- )
147
- )
148
- patched_methods.append(
149
- patch.object(
150
- SMBus,
151
- "write_block_data",
152
- new_callable=lambda: SmbusSimulator.write_block_data,
153
- )
154
- )
155
- patched_methods.append(
156
- patch.object(
157
- SMBus, "write_byte", new_callable=lambda: SmbusSimulator.write_byte
158
- )
159
- )
160
- patched_methods.append(
161
- patch.object(
162
- SMBus,
163
- "write_word_data",
164
- new_callable=lambda: SmbusSimulator.write_word_data,
165
- )
166
- )
167
-
168
- for pm in patched_methods:
169
- pm.start()
103
+ def patch_methods(cls, simulator, methods):
104
+ patched = []
105
+ for i, method in enumerate(methods):
106
+ if hasattr(cls, method) and hasattr(simulator, method):
107
+ patched.append(patch.object(cls, method, new_callable=lambda: getattr(simulator, method)))
108
+ patched[i].start()
170
109
 
171
110
 
172
111
  def enable_simulation(sysfs_path: Path | str) -> None:
@@ -180,8 +119,23 @@ def enable_simulation(sysfs_path: Path | str) -> None:
180
119
  sysfs_path = Path(sysfs_path)
181
120
 
182
121
  set_sysfs_root(sysfs_path)
183
- enable_gpio_simulation()
184
- enable_smbus_simulation()
122
+ patch_methods(
123
+ GPIO,
124
+ GPIOSimulator,
125
+ ["__init__", "num_lines", "set_pin", "get_pin", "set_direction", "__str__"],
126
+ )
127
+ patch_methods(
128
+ SMBus,
129
+ SMBusSimulator,
130
+ [
131
+ "__init__",
132
+ "read_word_data",
133
+ "read_block_data",
134
+ "write_block_data",
135
+ "write_byte",
136
+ "write_word_data",
137
+ ],
138
+ )
185
139
 
186
140
 
187
141
  def register_gpio_simulations():
@@ -189,7 +143,7 @@ def register_gpio_simulations():
189
143
  register_gpio_sim(233, 2, EKFIdSimGpio(0x34, 0xA, 0x0, 0x1)) # CCU Rev 0
190
144
 
191
145
 
192
- class SocketSmbus(SimSmbus):
146
+ class SocketSmbus(SimSMBus):
193
147
  def __init__(self, host: str, port: int) -> None:
194
148
  self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
195
149
  self.sock.connect((host, port))