jmri_core.beans.roster_entry

RosterEntry — Pydantic data model for a stored locomotive.

Mirrors the fields in contracts/xml/schema/roster.xsd. XML serialization lives in jmri_core.persistence.roster_xml so this module stays purely declarative.

 1"""``RosterEntry`` — Pydantic data model for a stored locomotive.
 2
 3Mirrors the fields in ``contracts/xml/schema/roster.xsd``. XML serialization
 4lives in :mod:`jmri_core.persistence.roster_xml` so this module stays purely
 5declarative.
 6"""
 7from __future__ import annotations
 8
 9from pydantic import BaseModel, ConfigDict, Field
10
11
12class FunctionLabel(BaseModel):
13    """One function-key label (e.g. ``F2 → "Whistle"``)."""
14
15    model_config = ConfigDict(populate_by_name=True)
16
17    number: int = Field(ge=0, le=31)
18    label: str = ""
19    lockable: bool = True
20    image: str | None = None
21    selected_image: str | None = None
22
23
24class SoundLabel(BaseModel):
25    """One sound-event label."""
26
27    number: int = Field(ge=0)
28    label: str = ""
29
30
31class RosterEntry(BaseModel):
32    """A roster entry — what JMRI's ``RosterEntry`` carries."""
33
34    model_config = ConfigDict(populate_by_name=True)
35
36    # Identity
37    id: str
38    file_name: str = ""
39
40    # DCC
41    dcc_address: str = "3"
42    long_address: bool = False
43    protocol: str = "dcc_short"
44
45    # Decoder identity
46    decoder_family: str = ""
47    decoder_model: str = ""
48    decoder_comment: str = ""
49
50    # Display
51    road_name: str = ""
52    road_number: str = ""
53    mfg: str = ""
54    owner: str = ""
55    model: str = ""
56    comment: str = ""
57
58    # Operations
59    max_speed_pct: int = Field(default=100, ge=0, le=100)
60
61    # Imagery
62    image_file_path: str | None = None
63    icon_file_path: str | None = None
64    url: str | None = None
65    date_modified: str | None = None
66
67    # Function & sound labels
68    function_labels: list[FunctionLabel] = Field(default_factory=list)
69    sound_labels: list[SoundLabel] = Field(default_factory=list)
70
71    # Free-form attributes (JMRI <attributepairs>)
72    attributes: dict[str, str] = Field(default_factory=dict)
73
74    # Unmodeled child XML — for elements like ``<values>`` (CV state),
75    # ``<dateUpdated>``, custom developer/manufacturer fields, etc. that
76    # JMRI authors but we don't yet have typed bindings for. Stored as
77    # serialized XML strings so the model stays JSON-friendly. Preserved
78    # on round-trip so a load → save doesn't destroy decoder CV state.
79    preserved_xml: list[str] = Field(default_factory=list)
class FunctionLabel(pydantic.main.BaseModel):
13class FunctionLabel(BaseModel):
14    """One function-key label (e.g. ``F2 → "Whistle"``)."""
15
16    model_config = ConfigDict(populate_by_name=True)
17
18    number: int = Field(ge=0, le=31)
19    label: str = ""
20    lockable: bool = True
21    image: str | None = None
22    selected_image: str | None = None

One function-key label (e.g. F2 → "Whistle").

number: int = PydanticUndefined
label: str = ''
lockable: bool = True
image: str | None = None
selected_image: str | None = None
class SoundLabel(pydantic.main.BaseModel):
25class SoundLabel(BaseModel):
26    """One sound-event label."""
27
28    number: int = Field(ge=0)
29    label: str = ""

One sound-event label.

number: int = PydanticUndefined
label: str = ''
class RosterEntry(pydantic.main.BaseModel):
32class RosterEntry(BaseModel):
33    """A roster entry — what JMRI's ``RosterEntry`` carries."""
34
35    model_config = ConfigDict(populate_by_name=True)
36
37    # Identity
38    id: str
39    file_name: str = ""
40
41    # DCC
42    dcc_address: str = "3"
43    long_address: bool = False
44    protocol: str = "dcc_short"
45
46    # Decoder identity
47    decoder_family: str = ""
48    decoder_model: str = ""
49    decoder_comment: str = ""
50
51    # Display
52    road_name: str = ""
53    road_number: str = ""
54    mfg: str = ""
55    owner: str = ""
56    model: str = ""
57    comment: str = ""
58
59    # Operations
60    max_speed_pct: int = Field(default=100, ge=0, le=100)
61
62    # Imagery
63    image_file_path: str | None = None
64    icon_file_path: str | None = None
65    url: str | None = None
66    date_modified: str | None = None
67
68    # Function & sound labels
69    function_labels: list[FunctionLabel] = Field(default_factory=list)
70    sound_labels: list[SoundLabel] = Field(default_factory=list)
71
72    # Free-form attributes (JMRI <attributepairs>)
73    attributes: dict[str, str] = Field(default_factory=dict)
74
75    # Unmodeled child XML — for elements like ``<values>`` (CV state),
76    # ``<dateUpdated>``, custom developer/manufacturer fields, etc. that
77    # JMRI authors but we don't yet have typed bindings for. Stored as
78    # serialized XML strings so the model stays JSON-friendly. Preserved
79    # on round-trip so a load → save doesn't destroy decoder CV state.
80    preserved_xml: list[str] = Field(default_factory=list)

A roster entry — what JMRI's RosterEntry carries.

id: str = PydanticUndefined
file_name: str = ''
dcc_address: str = '3'
long_address: bool = False
protocol: str = 'dcc_short'
decoder_family: str = ''
decoder_model: str = ''
decoder_comment: str = ''
road_name: str = ''
road_number: str = ''
mfg: str = ''
owner: str = ''
model: str = ''
comment: str = ''
max_speed_pct: int = 100
image_file_path: str | None = None
icon_file_path: str | None = None
url: str | None = None
date_modified: str | None = None
function_labels: list[FunctionLabel] = PydanticUndefined
sound_labels: list[SoundLabel] = PydanticUndefined
attributes: dict[str, str] = PydanticUndefined
preserved_xml: list[str] = PydanticUndefined