jmri_core.beans.signal_head
SignalHead — a single signal-light unit with a fixed appearance set.
1"""``SignalHead`` — a single signal-light unit with a fixed appearance set.""" 2from __future__ import annotations 3 4from dataclasses import dataclass 5 6from jmri_core.bean import AbstractNamedBean 7from jmri_core.manager import Manager 8from jmri_core.state import SignalAppearance 9 10 11@dataclass(frozen=True, slots=True) 12class SignalHeadChange: 13 previous: SignalAppearance 14 current: SignalAppearance 15 16 17class SignalHead(AbstractNamedBean): 18 """A single signal head (one physical signal, one face). 19 20 The ``held`` flag forces the head to RED regardless of its commanded 21 appearance — used by the dispatcher to override aspect logic. 22 """ 23 24 __slots__ = ("_appearance", "_held") 25 26 type_letter = "H" 27 28 def __init__(self, system_name: str, user_name: str | None = None) -> None: 29 super().__init__(system_name, user_name) 30 self._appearance: SignalAppearance = SignalAppearance.DARK 31 self._held: bool = False 32 33 @property 34 def appearance(self) -> SignalAppearance: 35 return SignalAppearance.RED if self._held else self._appearance 36 37 @property 38 def commanded_appearance(self) -> SignalAppearance: 39 """The appearance ignoring ``held`` — what was last set explicitly.""" 40 return self._appearance 41 42 @property 43 def held(self) -> bool: 44 return self._held 45 46 async def set_appearance(self, appearance: SignalAppearance) -> None: 47 prev_effective = self.appearance 48 self._appearance = appearance 49 new_effective = self.appearance 50 if prev_effective != new_effective: 51 await self.state_changed.emit(SignalHeadChange(prev_effective, new_effective)) 52 53 async def set_held(self, held: bool) -> None: 54 if held == self._held: 55 return 56 prev_effective = self.appearance 57 self._held = held 58 new_effective = self.appearance 59 if prev_effective != new_effective: 60 await self.state_changed.emit(SignalHeadChange(prev_effective, new_effective)) 61 62 63class SignalHeadManager(Manager[SignalHead]): 64 type_letter = "H" 65 66 def _create(self, system_name: str) -> SignalHead: 67 return SignalHead(system_name)
@dataclass(frozen=True, slots=True)
class
SignalHeadChange:
12@dataclass(frozen=True, slots=True) 13class SignalHeadChange: 14 previous: SignalAppearance 15 current: SignalAppearance
SignalHeadChange( previous: jmri_core.state.SignalAppearance, current: jmri_core.state.SignalAppearance)
previous: jmri_core.state.SignalAppearance
current: jmri_core.state.SignalAppearance
18class SignalHead(AbstractNamedBean): 19 """A single signal head (one physical signal, one face). 20 21 The ``held`` flag forces the head to RED regardless of its commanded 22 appearance — used by the dispatcher to override aspect logic. 23 """ 24 25 __slots__ = ("_appearance", "_held") 26 27 type_letter = "H" 28 29 def __init__(self, system_name: str, user_name: str | None = None) -> None: 30 super().__init__(system_name, user_name) 31 self._appearance: SignalAppearance = SignalAppearance.DARK 32 self._held: bool = False 33 34 @property 35 def appearance(self) -> SignalAppearance: 36 return SignalAppearance.RED if self._held else self._appearance 37 38 @property 39 def commanded_appearance(self) -> SignalAppearance: 40 """The appearance ignoring ``held`` — what was last set explicitly.""" 41 return self._appearance 42 43 @property 44 def held(self) -> bool: 45 return self._held 46 47 async def set_appearance(self, appearance: SignalAppearance) -> None: 48 prev_effective = self.appearance 49 self._appearance = appearance 50 new_effective = self.appearance 51 if prev_effective != new_effective: 52 await self.state_changed.emit(SignalHeadChange(prev_effective, new_effective)) 53 54 async def set_held(self, held: bool) -> None: 55 if held == self._held: 56 return 57 prev_effective = self.appearance 58 self._held = held 59 new_effective = self.appearance 60 if prev_effective != new_effective: 61 await self.state_changed.emit(SignalHeadChange(prev_effective, new_effective))
A single signal head (one physical signal, one face).
The held flag forces the head to RED regardless of its commanded
appearance — used by the dispatcher to override aspect logic.
appearance: jmri_core.state.SignalAppearance
commanded_appearance: jmri_core.state.SignalAppearance
38 @property 39 def commanded_appearance(self) -> SignalAppearance: 40 """The appearance ignoring ``held`` — what was last set explicitly.""" 41 return self._appearance
The appearance ignoring held — what was last set explicitly.
Inherited Members
64class SignalHeadManager(Manager[SignalHead]): 65 type_letter = "H" 66 67 def _create(self, system_name: str) -> SignalHead: 68 return SignalHead(system_name)
Per-type bean registry. Subclass and override _create().
Attributes set on the class describe the bean type:
type_letter— single uppercase letter the bean class owns (e.g."T"for Turnout). Must be set by subclass.system_prefix— connection prefix (e.g."L"). Set per-instance because one bean class can have many connection-specific managers.