ekfsm 0.12.0.post1__py3-none-any.whl → 0.13.0a160.post4__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.

@@ -0,0 +1,41 @@
1
+ id: 64
2
+ name: "EKF SE5-CLUB"
3
+ slot_type: CPCI_S0_PER
4
+ children:
5
+ - device_type: I2CMux
6
+ name: "MUX"
7
+ addr: 0x70
8
+ slot_coding_mask: 0x07
9
+ children:
10
+ - device_type: MuxChannel
11
+ name: "CH00"
12
+ channel_id: 0
13
+ children:
14
+ - device_type: EKFIdentificationIOExpander
15
+ name: "GPIO"
16
+ addr: 0x3D
17
+ provides:
18
+ inventory:
19
+ - revision
20
+ - device_type: EKF_EEPROM
21
+ name: "EEPROM"
22
+ addr: 0x55
23
+ provides:
24
+ inventory:
25
+ - vendor
26
+ - serial
27
+ - model
28
+ - repaired_at
29
+ - manufactured_at
30
+ - device_type: MuxChannel
31
+ name: "CH01"
32
+ channel_id: 1
33
+ children:
34
+ - device_type: MuxChannel
35
+ name: "CH02"
36
+ channel_id: 2
37
+ children:
38
+ - device_type: MuxChannel
39
+ name: "CH03"
40
+ channel_id: 3
41
+ children:
@@ -0,0 +1,41 @@
1
+ id: 61
2
+ name: "EKF SN4-DJEMBE"
3
+ slot_type: CPCI_S0_PER
4
+ children:
5
+ - device_type: I2CMux
6
+ name: "MUX"
7
+ addr: 0x70
8
+ slot_coding_mask: 0x07
9
+ children:
10
+ - device_type: MuxChannel
11
+ name: "CH00"
12
+ channel_id: 0
13
+ children:
14
+ - device_type: EKFIdentificationIOExpander
15
+ name: "GPIO"
16
+ addr: 0x3D
17
+ provides:
18
+ inventory:
19
+ - revision
20
+ - device_type: EKF_EEPROM
21
+ name: "EEPROM"
22
+ addr: 0x55
23
+ provides:
24
+ inventory:
25
+ - vendor
26
+ - serial
27
+ - model
28
+ - repaired_at
29
+ - manufactured_at
30
+ - device_type: MuxChannel
31
+ name: "CH01"
32
+ channel_id: 1
33
+ children:
34
+ - device_type: MuxChannel
35
+ name: "CH02"
36
+ channel_id: 2
37
+ children:
38
+ - device_type: MuxChannel
39
+ name: "CH03"
40
+ channel_id: 3
41
+ children:
@@ -1,5 +1,5 @@
1
- id: HDRC300
2
- name: "HITRON HDRC-300"
1
+ id: HDRC300S-110J-D120E(N)
2
+ name: "Hitron HDRC-300S"
3
3
  slot_type: CPCI_S0_PSU
4
4
  children:
5
5
  - device_type: PmBus
@@ -15,6 +15,10 @@ children:
15
15
  main:
16
16
  - voltage: in1_input
17
17
  - current: curr1_input
18
+ - status: status0_input
18
19
  sby:
19
20
  - voltage: in2_input
20
21
  - current: curr2_input
22
+ - status: status1_input
23
+ th:
24
+ - temperature: temp1_input
ekfsm/cli.py CHANGED
@@ -75,9 +75,12 @@ def cli(verbose, debug, sysfs, config):
75
75
  @click.option("--revision", "-r", is_flag=False, help="Write chassis revision")
76
76
  @click.option("--model", "-m", is_flag=False, help="Write chassis model")
77
77
  @click.option("--custom", "-c", is_flag=False, help="Write chassis custom information")
78
- def write(serial, unit, vendor, revision, model, custom):
78
+ @click.option("--version", "-v", is_flag=True, help="Write schema version")
79
+ def write(serial, unit, vendor, revision, model, custom, version):
79
80
  """Write data to the system"""
80
81
  chassis = sm.ccu.chassis_inventory
82
+ eeprom = sm.ccu.mux.ch00.eeprom
83
+
81
84
  if serial:
82
85
  chassis.write_serial(serial)
83
86
  if unit:
@@ -90,6 +93,8 @@ def write(serial, unit, vendor, revision, model, custom):
90
93
  chassis.write_model(model)
91
94
  if custom:
92
95
  chassis.write_customer_area(custom)
96
+ if version:
97
+ eeprom.write_version(version)
93
98
 
94
99
 
95
100
  @cli.command()
ekfsm/config.py CHANGED
@@ -7,6 +7,7 @@ from typing import Any, List, Tuple
7
7
  schema_str = """
8
8
  system_config:
9
9
  name: str()
10
+ aggregates: map(required=False)
10
11
  slots: list(include('slot'))
11
12
  ---
12
13
  slot:
ekfsm/core/components.py CHANGED
@@ -11,16 +11,17 @@ if TYPE_CHECKING:
11
11
  from .slots import Slot
12
12
 
13
13
 
14
- class SystemComponent(NodeMixin):
14
+ class SysTree(NodeMixin):
15
15
  """
16
16
  Base class for all system components including Hardware Modules and Devices.
17
17
  """
18
18
 
19
- def __init__(self, name: str):
19
+ def __init__(self, name: str, abort: bool = False) -> None:
20
20
  from ekfsm.log import ekfsm_logger
21
21
 
22
22
  self.logger = ekfsm_logger(name)
23
23
  self.name = name
24
+ self.abort = abort
24
25
 
25
26
  def _render_tree(self) -> str:
26
27
  output = ""
@@ -35,18 +36,26 @@ class SystemComponent(NodeMixin):
35
36
  print(self)
36
37
 
37
38
 
38
- class HwModule(SystemComponent):
39
+ class HwModule(SysTree):
39
40
  """
40
41
  A HwModule represents an instantiation of a specifc hw board type,
41
42
  for example an instance of an EKF SC9 board.
42
43
  """
43
44
 
44
- def __init__(self, instance_name: str, config: dict, slot: Slot) -> None:
45
+ def __init__(
46
+ self,
47
+ instance_name: str,
48
+ config: dict,
49
+ slot: Slot,
50
+ abort: bool = False,
51
+ *args,
52
+ **kwargs,
53
+ ) -> None:
45
54
  from ekfsm.core.utils import deserialize_hardware_tree
46
55
 
47
56
  from .slots import SlotType
48
57
 
49
- super().__init__(instance_name)
58
+ super().__init__(instance_name, abort=abort)
50
59
  self._slot: Slot = slot
51
60
  self.config = config
52
61
 
ekfsm/core/utils.py CHANGED
@@ -16,11 +16,11 @@ class BoardDictImporter:
16
16
  def __init__(self, nodecls=AnyNode):
17
17
  self.nodecls = nodecls
18
18
 
19
- def import_(self, logger: logging.Logger, data, parent=None):
19
+ def import_(self, logger: logging.Logger, data, parent=None, abort: bool = False):
20
20
  """Import tree from `data`."""
21
- return self.__import(logger, data, parent=parent)
21
+ return self.__import(logger, data, parent=parent, abort=abort)
22
22
 
23
- def __import(self, logger: logging.Logger, data, parent=None):
23
+ def __import(self, logger: logging.Logger, data, parent=None, abort: bool = False):
24
24
  from .components import HwModule
25
25
 
26
26
  device_type = data.get("device_type")
@@ -30,6 +30,7 @@ class BoardDictImporter:
30
30
 
31
31
  children = data.pop("children", [])
32
32
  if parent is not None and isinstance(parent, HwModule):
33
+ # ???
33
34
  pass
34
35
 
35
36
  node = nodecls(parent=parent, **data)
@@ -38,11 +39,17 @@ class BoardDictImporter:
38
39
  for child in children:
39
40
  try:
40
41
  logger.debug(f"Importing sub device {child}")
41
- self.__import(logger, child, parent=node)
42
+ self.__import(logger, child, parent=node, abort=abort)
42
43
  except Exception as e:
43
- logger.error(
44
- f"Failed to import sub device {child}: {e}. continue anyway"
45
- )
44
+ if abort:
45
+ logger.error(
46
+ f"Failed to import sub device {child}: {e}. aborting"
47
+ )
48
+ raise e
49
+ else:
50
+ logger.error(
51
+ f"Failed to import sub device {child}: {e}. continue anyway"
52
+ )
46
53
  return node
47
54
 
48
55
 
@@ -50,7 +57,9 @@ def deserialize_hardware_tree(
50
57
  logger: logging.Logger, data: dict, parent: "HwModule"
51
58
  ) -> tuple[str, str, str, list["Device"]]:
52
59
  importer = BoardDictImporter()
60
+ abort = parent.abort
53
61
 
62
+ # better use schema extension for this
54
63
  id = data.pop("id", None)
55
64
  if id is None:
56
65
  raise ConfigError("Board configuration must contain `id`")
@@ -67,11 +76,17 @@ def deserialize_hardware_tree(
67
76
  for child in children:
68
77
  try:
69
78
  logger.debug(f"Importing top level device {child}")
70
- node = importer.import_(logger, child, parent=parent)
79
+ node = importer.import_(logger, child, parent=parent, abort=abort)
71
80
  devices.append(node)
72
81
  except Exception as e:
73
- logger.error(
74
- f"Failed to import top level device {child}: {e}. continue anyway"
75
- )
82
+ if abort:
83
+ logger.error(
84
+ f"Failed to import top level device {child}: {e}. aborting"
85
+ )
86
+ raise e
87
+ else:
88
+ logger.error(
89
+ f"Failed to import top level device {child}: {e}. continue anyway"
90
+ )
76
91
 
77
92
  return id, name, slot_type, devices
ekfsm/devices/__init__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from ekfsm.devices.generic import Device
2
- from ekfsm.devices.hwmon import HWMON
2
+ from ekfsm.devices.coretemp import CoreTemp
3
3
  from ekfsm.devices.smbios import SMBIOS
4
4
  from .eeprom import EEPROM, EKF_EEPROM, EKF_CCU_EEPROM
5
- from .pmbus import PmBus
5
+ from .pmbus import PmBus, PsuStatus
6
6
  from .gpio import GPIO, EKFIdentificationIOExpander, GPIOExpander
7
7
  from .ekf_sur_led import EKFSurLed
8
8
  from .ekf_ccu_uc import EKFCcuUc
@@ -21,8 +21,9 @@ CLASS_MAP = {
21
21
  "EKF_CCU_EEPROM": EKF_CCU_EEPROM,
22
22
  "EKFCcuUc": EKFCcuUc,
23
23
  "PmBus": PmBus,
24
+ "PsuStatus": PsuStatus,
24
25
  "SMBIOS": SMBIOS,
25
- "HWMON": HWMON,
26
+ "HWMON": CoreTemp,
26
27
  "EKFSurLed": EKFSurLed,
27
28
  "IIOThermalHumidity": IIOThermalHumidity,
28
29
  }
@@ -7,7 +7,7 @@ from ekfsm.core.sysfs import SYSFS_ROOT
7
7
  from ekfsm.devices.generic import Device
8
8
 
9
9
  # Path to the root of the HWMON sysfs filesystem
10
- HWMON_ROOT = SYSFS_ROOT / Path('class/hwmon')
10
+ HWMON_ROOT = SYSFS_ROOT / Path("class/hwmon")
11
11
 
12
12
 
13
13
  def find_core_temp_dir(hwmon_dir) -> Path:
@@ -24,19 +24,19 @@ def find_core_temp_dir(hwmon_dir) -> Path:
24
24
  FileNotFoundError: If no coretemp directory is found
25
25
  """
26
26
  # List all 'name' files in each subdirectory of hwmon_dir
27
- name_files = glob.glob(os.path.join(hwmon_dir, '*', 'name'))
27
+ name_files = glob.glob(os.path.join(hwmon_dir, "*", "name"))
28
28
 
29
29
  # Search for the file containing "coretemp"
30
30
  for name_file in name_files:
31
- with open(name_file, 'r') as file:
32
- if file.readline().strip() == 'coretemp':
31
+ with open(name_file, "r") as file:
32
+ if file.readline().strip() == "coretemp":
33
33
  # Return the directory containing this file
34
34
  return Path(os.path.dirname(name_file))
35
35
 
36
36
  raise FileNotFoundError("No coretemp directory found")
37
37
 
38
38
 
39
- class HWMON(Device):
39
+ class CoreTemp(Device):
40
40
  """
41
41
  A class to represent the HWMON device.
42
42
 
@@ -55,7 +55,9 @@ class HWMON(Device):
55
55
  ):
56
56
  from ekfsm.core.sysfs import SysFSDevice, SYSFS_ROOT
57
57
 
58
- self.sysfs_device: SysFSDevice = SysFSDevice(find_core_temp_dir(SYSFS_ROOT / Path('class/hwmon')))
58
+ self.sysfs_device: SysFSDevice = SysFSDevice(
59
+ find_core_temp_dir(SYSFS_ROOT / Path("class/hwmon"))
60
+ )
59
61
 
60
62
  super().__init__(name, parent, None, *args, **kwargs)
61
63
 
ekfsm/devices/eeprom.py CHANGED
@@ -14,7 +14,7 @@ from datetime import date
14
14
  from typing import Any, Callable, Literal, Sequence
15
15
  from functools import wraps
16
16
 
17
- from ekfsm.core.components import SystemComponent
17
+ from ekfsm.core.components import SysTree
18
18
  from ekfsm.core.probe import ProbeableDevice
19
19
 
20
20
  from .generic import Device
@@ -81,7 +81,7 @@ class EEPROM(Device):
81
81
  def __init__(
82
82
  self,
83
83
  name: str,
84
- parent: SystemComponent | None = None,
84
+ parent: SysTree | None = None,
85
85
  *args,
86
86
  **kwargs,
87
87
  ):
@@ -740,7 +740,7 @@ class EKF_EEPROM(Validatable_EEPROM, ProbeableDevice):
740
740
  return get_crc16_xmodem(self._data)
741
741
 
742
742
  def probe(self, *args, **kwargs):
743
- return self.root.id == self.model()
743
+ return self.hw_module.id == self.model()
744
744
 
745
745
 
746
746
  class EKF_CCU_EEPROM(EKF_EEPROM):
@@ -764,7 +764,7 @@ class EKF_CCU_EEPROM(EKF_EEPROM):
764
764
  _unit_length = 1
765
765
 
766
766
  _customer_area_start = 190
767
- _customer_area_length = 64
767
+ _customer_area_length = 63
768
768
 
769
769
  def __init__(
770
770
  self,
@@ -865,6 +865,7 @@ class EKF_CCU_EEPROM(EKF_EEPROM):
865
865
  -------
866
866
  The vendor of the chassis.
867
867
  """
868
+
868
869
  return (
869
870
  self._content[
870
871
  self._cvendor_index_start : self._cvendor_index_start
@@ -1002,6 +1003,42 @@ class EKF_CCU_EEPROM(EKF_EEPROM):
1002
1003
  unit = compute_int_from_bytes([area])
1003
1004
  return unit
1004
1005
 
1006
+ @validated
1007
+ def version(self) -> int:
1008
+ """
1009
+ Get the version of the EEPROM data scheme.
1010
+
1011
+ Note
1012
+ ----
1013
+ If undefined, the version is set to 255 and then defaults to 0.
1014
+
1015
+ Returns
1016
+ -------
1017
+ The version of the EEPROM data scheme.
1018
+ """
1019
+ version = self._content[self._ccrc_pos_start - 1]
1020
+ return version
1021
+
1022
+ def write_version(self, version: int) -> None:
1023
+ """
1024
+ Write the version of the EEPROM data scheme.
1025
+
1026
+ Parameters
1027
+ ----------
1028
+ version
1029
+ The version of the EEPROM data scheme.
1030
+ """
1031
+ if version < 0 or version > 255:
1032
+ raise ValueError("Version must be between 0 and 255")
1033
+
1034
+ if version == 255:
1035
+ logger.warning("Version 255 is undefined, setting to 0")
1036
+ version = 0
1037
+
1038
+ version_bytes = version.to_bytes(1, byteorder="little")
1039
+ logger.info(f"Writing version {version}")
1040
+ self.write(version_bytes, self._ccrc_pos_start - 1)
1041
+
1005
1042
  def write_unit(self, unit: int) -> None:
1006
1043
  """
1007
1044
  Write the subsystem unit number.
@@ -2,7 +2,7 @@ from .generic import Device
2
2
  from smbus2 import SMBus
3
3
  from enum import Enum
4
4
  from typing import Tuple
5
- from ekfsm.core.components import SystemComponent
5
+ from ekfsm.core.components import SysTree
6
6
  from ..exceptions import AcquisitionError
7
7
  from .imu import ImuSample
8
8
  import struct
@@ -35,7 +35,7 @@ class EKFCcuUc(Device):
35
35
  def __init__(
36
36
  self,
37
37
  name: str,
38
- parent: SystemComponent | None,
38
+ parent: SysTree | None,
39
39
  *args,
40
40
  **kwargs,
41
41
  ):
@@ -1,5 +1,5 @@
1
1
  from .gpio import GPIOExpander
2
- from ekfsm.core.components import SystemComponent
2
+ from ekfsm.core.components import SysTree
3
3
 
4
4
 
5
5
  class EKFSurLed(GPIOExpander):
@@ -10,7 +10,7 @@ class EKFSurLed(GPIOExpander):
10
10
  def __init__(
11
11
  self,
12
12
  name: str,
13
- parent: SystemComponent | None,
13
+ parent: SysTree | None,
14
14
  *args,
15
15
  **kwargs,
16
16
  ):
ekfsm/devices/generic.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
  from munch import Munch
3
- from ekfsm.core.components import SystemComponent
3
+ from ekfsm.core.components import SysTree
4
4
  from ekfsm.core.sysfs import SysFSDevice, sysfs_root
5
5
  from ekfsm.exceptions import ConfigError
6
6
  from pathlib import Path
@@ -9,7 +9,7 @@ if TYPE_CHECKING:
9
9
  from ekfsm.core.components import HwModule
10
10
 
11
11
 
12
- class Device(SystemComponent):
12
+ class Device(SysTree):
13
13
  """
14
14
  A generic device.
15
15
  """
@@ -17,12 +17,13 @@ class Device(SystemComponent):
17
17
  def __init__(
18
18
  self,
19
19
  name: str,
20
- parent: SystemComponent | None = None,
20
+ parent: SysTree | None = None,
21
21
  children: list["Device"] | None = None,
22
+ abort: bool = False,
22
23
  *args,
23
- **kwargs
24
+ **kwargs,
24
25
  ):
25
- super().__init__(name)
26
+ super().__init__(name, abort=abort)
26
27
  self.parent = parent
27
28
  self.device_args = kwargs
28
29
  self.logger.debug(f"Device: {name} {kwargs}")
@@ -30,21 +31,21 @@ class Device(SystemComponent):
30
31
  if children:
31
32
  self.children = children
32
33
 
33
- if not hasattr(self, "sysfs_device"):
34
+ # needs to be set during init because root will be changed after tree is complete
35
+ self.hw_module = self.root
36
+
37
+ # i2c initialization
38
+ if not hasattr(self, "sysfs_device") or self.sysfs_device is None:
34
39
  self.sysfs_device: SysFSDevice | None = None
35
40
 
41
+ # post init
36
42
  self._provides_attrs = kwargs.get("provides", {})
37
-
38
43
  self.provides = self.__post_init__(Munch(self._provides_attrs))
39
44
 
40
45
  def __post_init__(self, provides: Munch) -> Munch:
41
- return self.__init_dynamic_attrs__(provides)
42
-
43
- def __init_dynamic_attrs__(self, provides: Munch) -> Munch:
44
-
45
46
  for key, fields in provides.items():
46
47
  if isinstance(fields, dict):
47
- provides[key] = self.__init_dynamic_attrs__(Munch(fields))
48
+ provides[key] = self.__post_init__(Munch(fields))
48
49
  elif isinstance(fields, list | str):
49
50
  provides[key] = Munch()
50
51
 
@@ -91,14 +92,18 @@ class Device(SystemComponent):
91
92
  return None
92
93
 
93
94
  @property
94
- def hw_module(self) -> 'HwModule':
95
+ def hw_module(self) -> "HwModule":
95
96
  from ekfsm.core.components import HwModule
96
97
 
97
- if isinstance(self.root, HwModule):
98
- return self.root
98
+ if isinstance(self._hw_module, HwModule):
99
+ return self._hw_module
99
100
  else:
100
101
  raise RuntimeError("Device is not a child of HwModule")
101
102
 
103
+ @hw_module.setter
104
+ def hw_module(self, hw_module: "HwModule") -> None:
105
+ self._hw_module = hw_module
106
+
102
107
  def get_i2c_chip_addr(self) -> int:
103
108
  assert self.parent is not None
104
109
 
ekfsm/devices/gpio.py CHANGED
@@ -8,7 +8,7 @@ from gpiod.chip import LineSettings
8
8
  from gpiod.line import Direction, Value
9
9
  from more_itertools import first_true
10
10
 
11
- from ekfsm.core.components import SystemComponent
11
+ from ekfsm.core.components import SysTree
12
12
  from ekfsm.exceptions import GPIOError
13
13
  from ekfsm.log import ekfsm_logger
14
14
 
@@ -55,7 +55,7 @@ class GPIO(Device):
55
55
  def __init__(
56
56
  self,
57
57
  name: str,
58
- parent: SystemComponent | None = None,
58
+ parent: SysTree | None = None,
59
59
  *args,
60
60
  **kwargs,
61
61
  ):
@@ -83,7 +83,7 @@ class GPIO(Device):
83
83
 
84
84
  def _find_gpio_dev(
85
85
  self,
86
- parent: SystemComponent | None = None,
86
+ parent: SysTree | None = None,
87
87
  *args,
88
88
  **kwargs,
89
89
  ) -> tuple[int, int]:
@@ -186,7 +186,7 @@ class GPIOExpander(GPIO):
186
186
  def __init__(
187
187
  self,
188
188
  name: str,
189
- parent: SystemComponent | None,
189
+ parent: SysTree | None,
190
190
  *args,
191
191
  **kwargs,
192
192
  ):
@@ -203,7 +203,7 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
203
203
  def __init__(
204
204
  self,
205
205
  name: str,
206
- parent: SystemComponent | None,
206
+ parent: SysTree | None,
207
207
  *args,
208
208
  **kwargs,
209
209
  ):
@@ -212,11 +212,11 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
212
212
  def probe(self, *args, **kwargs) -> bool:
213
213
  from ekfsm.core import HwModule
214
214
 
215
- assert isinstance(self.root, HwModule)
215
+ assert isinstance(self.hw_module, HwModule)
216
216
  id, _ = self.read_board_id_rev()
217
217
  self.logger.debug(f"Probing EKFIdentificationIOExpander: {id}")
218
218
 
219
- return self.root.id == id
219
+ return self.hw_module.id == id
220
220
 
221
221
  def read_board_id_rev(self) -> tuple[int, int]:
222
222
  for pin in range(6, 8):
@@ -1,6 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
- from ekfsm.core.components import SystemComponent
3
+ from ekfsm.core.components import SysTree
4
4
  from ekfsm.log import ekfsm_logger
5
5
 
6
6
  from ..core.sysfs import SysFSDevice
@@ -18,7 +18,7 @@ class IIOThermalHumidity(Device):
18
18
  def __init__(
19
19
  self,
20
20
  name: str,
21
- parent: SystemComponent | None = None,
21
+ parent: SysTree | None = None,
22
22
  children: list[Device] | None = None,
23
23
  *args,
24
24
  **kwargs,
ekfsm/devices/mux.py CHANGED
@@ -1,4 +1,4 @@
1
- from ekfsm.core.components import SystemComponent
1
+ from ekfsm.core.components import SysTree
2
2
 
3
3
  from ..core.sysfs import SysFSDevice
4
4
  from .generic import Device
@@ -9,7 +9,7 @@ class MuxChannel(Device):
9
9
  self,
10
10
  name: str,
11
11
  channel_id: int,
12
- parent: 'I2CMux',
12
+ parent: "I2CMux",
13
13
  children: list[Device] | None = None,
14
14
  *args,
15
15
  **kwargs,
@@ -28,7 +28,7 @@ class I2CMux(Device):
28
28
  def __init__(
29
29
  self,
30
30
  name: str,
31
- parent: SystemComponent | None = None,
31
+ parent: SysTree | None = None,
32
32
  children: list[MuxChannel] | None = None,
33
33
  *args,
34
34
  **kwargs,