jmri_core.beans.sensor

Sensor — a layout input (block detector, button, switch).

 1"""``Sensor`` — a layout input (block detector, button, switch)."""
 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 SensorState
 9
10
11@dataclass(frozen=True, slots=True)
12class SensorStateChange:
13    previous: SensorState
14    current: SensorState
15
16
17class Sensor(AbstractNamedBean):
18    """Generic layout input. Drivers call :meth:`_set_known_state` on receipt.
19
20    Sensors are write-only from the layout's perspective — the application
21    doesn't *command* them, only observes. The ``inverted`` flag flips the
22    semantic meaning of the raw hardware bit.
23    """
24
25    __slots__ = ("_known_state", "inverted")
26
27    type_letter = "S"
28
29    def __init__(self, system_name: str, user_name: str | None = None) -> None:
30        super().__init__(system_name, user_name)
31        self._known_state: SensorState = SensorState.UNKNOWN
32        self.inverted: bool = False
33
34    @property
35    def known_state(self) -> SensorState:
36        return self._known_state
37
38    async def _set_known_state(self, state: SensorState) -> None:
39        prev = self._known_state
40        if prev == state:
41            return
42        self._known_state = state
43        await self.state_changed.emit(SensorStateChange(prev, state))
44
45    @property
46    def is_active(self) -> bool:
47        return self._known_state == SensorState.ACTIVE
48
49
50class SensorManager(Manager[Sensor]):
51    type_letter = "S"
52
53    def _create(self, system_name: str) -> Sensor:
54        return Sensor(system_name)
@dataclass(frozen=True, slots=True)
class SensorStateChange:
12@dataclass(frozen=True, slots=True)
13class SensorStateChange:
14    previous: SensorState
15    current: SensorState
SensorStateChange( previous: jmri_core.state.SensorState, current: jmri_core.state.SensorState)
class Sensor(jmri_core.bean.AbstractNamedBean):
18class Sensor(AbstractNamedBean):
19    """Generic layout input. Drivers call :meth:`_set_known_state` on receipt.
20
21    Sensors are write-only from the layout's perspective — the application
22    doesn't *command* them, only observes. The ``inverted`` flag flips the
23    semantic meaning of the raw hardware bit.
24    """
25
26    __slots__ = ("_known_state", "inverted")
27
28    type_letter = "S"
29
30    def __init__(self, system_name: str, user_name: str | None = None) -> None:
31        super().__init__(system_name, user_name)
32        self._known_state: SensorState = SensorState.UNKNOWN
33        self.inverted: bool = False
34
35    @property
36    def known_state(self) -> SensorState:
37        return self._known_state
38
39    async def _set_known_state(self, state: SensorState) -> None:
40        prev = self._known_state
41        if prev == state:
42            return
43        self._known_state = state
44        await self.state_changed.emit(SensorStateChange(prev, state))
45
46    @property
47    def is_active(self) -> bool:
48        return self._known_state == SensorState.ACTIVE

Generic layout input. Drivers call _set_known_state() on receipt.

Sensors are write-only from the layout's perspective — the application doesn't command them, only observes. The inverted flag flips the semantic meaning of the raw hardware bit.

Sensor(system_name: str, user_name: str | None = None)
30    def __init__(self, system_name: str, user_name: str | None = None) -> None:
31        super().__init__(system_name, user_name)
32        self._known_state: SensorState = SensorState.UNKNOWN
33        self.inverted: bool = False
type_letter = 'S'
inverted: bool
known_state: jmri_core.state.SensorState
35    @property
36    def known_state(self) -> SensorState:
37        return self._known_state
is_active: bool
46    @property
47    def is_active(self) -> bool:
48        return self._known_state == SensorState.ACTIVE
51class SensorManager(Manager[Sensor]):
52    type_letter = "S"
53
54    def _create(self, system_name: str) -> Sensor:
55        return Sensor(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.
type_letter = 'S'