jmri-client 5.0.0 → 5.1.0
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.
- package/dist/browser/jmri-client.js +88 -28
- package/dist/cjs/index.js +88 -28
- package/dist/esm/index.js +88 -28
- package/dist/types/client.d.ts +9 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/managers/roster-manager.d.ts +9 -1
- package/dist/types/mocks/mock-data.d.ts +30 -6
- package/dist/types/mocks/mock-response-manager.d.ts +7 -2
- package/dist/types/types/jmri-messages.d.ts +22 -0
- package/docs/API.md +8 -0
- package/docs/MOCK_MODE.md +15 -9
- package/package.json +1 -1
|
@@ -723,8 +723,8 @@ var mockData = {
|
|
|
723
723
|
{ "name": "F4", "label": "Dynamic Brake", "lockable": true, "icon": null, "selectedIcon": null },
|
|
724
724
|
{ "name": "F5", "label": null, "lockable": false, "icon": null, "selectedIcon": null }
|
|
725
725
|
],
|
|
726
|
-
"attributes": [],
|
|
727
|
-
"rosterGroups": []
|
|
726
|
+
"attributes": [{ "name": "RosterGroup:diesels", "value": "yes" }],
|
|
727
|
+
"rosterGroups": ["diesels"]
|
|
728
728
|
},
|
|
729
729
|
"id": 1
|
|
730
730
|
},
|
|
@@ -754,8 +754,8 @@ var mockData = {
|
|
|
754
754
|
{ "name": "F3", "label": "Steam", "lockable": true, "icon": null, "selectedIcon": null },
|
|
755
755
|
{ "name": "F4", "label": null, "lockable": false, "icon": null, "selectedIcon": null }
|
|
756
756
|
],
|
|
757
|
-
"attributes": [],
|
|
758
|
-
"rosterGroups": []
|
|
757
|
+
"attributes": [{ "name": "RosterGroup:steam", "value": "yes" }],
|
|
758
|
+
"rosterGroups": ["steam"]
|
|
759
759
|
},
|
|
760
760
|
"id": 2
|
|
761
761
|
},
|
|
@@ -786,13 +786,19 @@ var mockData = {
|
|
|
786
786
|
{ "name": "F4", "label": null, "lockable": false, "icon": null, "selectedIcon": null },
|
|
787
787
|
{ "name": "F5", "label": "Mars Light", "lockable": true, "icon": null, "selectedIcon": null }
|
|
788
788
|
],
|
|
789
|
-
"attributes": [],
|
|
790
|
-
"rosterGroups": []
|
|
789
|
+
"attributes": [{ "name": "RosterGroup:diesels", "value": "yes" }],
|
|
790
|
+
"rosterGroups": ["diesels"]
|
|
791
791
|
},
|
|
792
792
|
"id": 3
|
|
793
793
|
}
|
|
794
794
|
]
|
|
795
795
|
},
|
|
796
|
+
"rosterGroup": {
|
|
797
|
+
"list": [
|
|
798
|
+
{ "type": "rosterGroup", "data": { "name": "diesels", "length": 2 } },
|
|
799
|
+
{ "type": "rosterGroup", "data": { "name": "steam", "length": 1 } }
|
|
800
|
+
]
|
|
801
|
+
},
|
|
796
802
|
"throttle": {
|
|
797
803
|
"acquire": {
|
|
798
804
|
"success": {
|
|
@@ -891,6 +897,7 @@ var mockData = {
|
|
|
891
897
|
// src/mocks/mock-response-manager.ts
|
|
892
898
|
var MockResponseManager = class {
|
|
893
899
|
constructor(options = {}) {
|
|
900
|
+
this.powerStateByPrefix = /* @__PURE__ */ new Map();
|
|
894
901
|
this.throttles = /* @__PURE__ */ new Map();
|
|
895
902
|
this.lights = /* @__PURE__ */ new Map([
|
|
896
903
|
["IL1", 4 /* OFF */],
|
|
@@ -919,6 +926,8 @@ var MockResponseManager = class {
|
|
|
919
926
|
return this.getPowerResponse(message);
|
|
920
927
|
case "roster":
|
|
921
928
|
return this.getRosterResponse(message);
|
|
929
|
+
case "rosterGroup":
|
|
930
|
+
return this.getRosterGroupResponse();
|
|
922
931
|
case "throttle":
|
|
923
932
|
return this.getThrottleResponse(message);
|
|
924
933
|
case "light":
|
|
@@ -940,34 +949,48 @@ var MockResponseManager = class {
|
|
|
940
949
|
return JSON.parse(JSON.stringify(mockData.hello));
|
|
941
950
|
}
|
|
942
951
|
/**
|
|
943
|
-
* Get power response
|
|
952
|
+
* Get power response, with optional per-prefix state tracking
|
|
944
953
|
*/
|
|
945
954
|
getPowerResponse(message) {
|
|
946
|
-
|
|
955
|
+
const prefix = message.data?.prefix;
|
|
956
|
+
if (message.method === "post" && message.data?.state !== void 0) {
|
|
957
|
+
if (prefix !== void 0) {
|
|
958
|
+
this.powerStateByPrefix.set(prefix, message.data.state);
|
|
959
|
+
return { type: "power", data: { state: message.data.state, prefix } };
|
|
960
|
+
}
|
|
947
961
|
this.powerState = message.data.state;
|
|
948
|
-
return {
|
|
949
|
-
type: "power",
|
|
950
|
-
data: { state: this.powerState }
|
|
951
|
-
};
|
|
962
|
+
return { type: "power", data: { state: this.powerState } };
|
|
952
963
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
data: { state
|
|
956
|
-
}
|
|
964
|
+
if (prefix !== void 0) {
|
|
965
|
+
const state = this.powerStateByPrefix.get(prefix) ?? 0 /* UNKNOWN */;
|
|
966
|
+
return { type: "power", data: { state, prefix } };
|
|
967
|
+
}
|
|
968
|
+
return { type: "power", data: { state: this.powerState } };
|
|
957
969
|
}
|
|
958
970
|
/**
|
|
959
|
-
* Get roster response
|
|
971
|
+
* Get roster response, optionally filtered by group
|
|
960
972
|
*/
|
|
961
973
|
getRosterResponse(message) {
|
|
962
974
|
if (message.type === "roster" && message.method === "list") {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
975
|
+
const all = JSON.parse(JSON.stringify(mockData.roster.list));
|
|
976
|
+
const group = message.params?.group;
|
|
977
|
+
if (group) {
|
|
978
|
+
return {
|
|
979
|
+
type: "roster",
|
|
980
|
+
data: all.filter((e) => e.data.rosterGroups?.includes(group))
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
return { type: "roster", data: all };
|
|
967
984
|
}
|
|
985
|
+
return { type: "roster", data: [] };
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Get roster group response
|
|
989
|
+
*/
|
|
990
|
+
getRosterGroupResponse() {
|
|
968
991
|
return {
|
|
969
|
-
type: "
|
|
970
|
-
data:
|
|
992
|
+
type: "rosterGroup",
|
|
993
|
+
data: JSON.parse(JSON.stringify(mockData.rosterGroup.list))
|
|
971
994
|
};
|
|
972
995
|
}
|
|
973
996
|
/**
|
|
@@ -986,13 +1009,11 @@ var MockResponseManager = class {
|
|
|
986
1009
|
F1: false,
|
|
987
1010
|
F2: false,
|
|
988
1011
|
F3: false,
|
|
989
|
-
F4: false
|
|
1012
|
+
F4: false,
|
|
1013
|
+
...data.prefix !== void 0 && { prefix: data.prefix }
|
|
990
1014
|
};
|
|
991
1015
|
this.throttles.set(throttleId, throttleState);
|
|
992
|
-
return {
|
|
993
|
-
type: "throttle",
|
|
994
|
-
data: { ...throttleState }
|
|
995
|
-
};
|
|
1016
|
+
return { type: "throttle", data: { ...throttleState } };
|
|
996
1017
|
}
|
|
997
1018
|
if (data.release !== void 0 && data.throttle) {
|
|
998
1019
|
this.throttles.delete(data.throttle);
|
|
@@ -1125,6 +1146,7 @@ var MockResponseManager = class {
|
|
|
1125
1146
|
*/
|
|
1126
1147
|
reset() {
|
|
1127
1148
|
this.powerState = 4 /* OFF */;
|
|
1149
|
+
this.powerStateByPrefix.clear();
|
|
1128
1150
|
this.throttles.clear();
|
|
1129
1151
|
this.lights = /* @__PURE__ */ new Map([
|
|
1130
1152
|
["IL1", 4 /* OFF */],
|
|
@@ -1608,6 +1630,32 @@ var RosterManager = class {
|
|
|
1608
1630
|
}
|
|
1609
1631
|
return results;
|
|
1610
1632
|
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Get all roster groups
|
|
1635
|
+
*/
|
|
1636
|
+
async getRosterGroups() {
|
|
1637
|
+
const message = {
|
|
1638
|
+
type: "rosterGroup",
|
|
1639
|
+
method: "list"
|
|
1640
|
+
};
|
|
1641
|
+
const response = await this.client.request(message);
|
|
1642
|
+
if (!response.data) return [];
|
|
1643
|
+
return response.data.map((wrapper) => wrapper.data);
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Get roster entries belonging to a specific group
|
|
1647
|
+
*/
|
|
1648
|
+
async getRosterEntriesByGroup(group) {
|
|
1649
|
+
const message = {
|
|
1650
|
+
type: "roster",
|
|
1651
|
+
method: "list",
|
|
1652
|
+
params: { group }
|
|
1653
|
+
};
|
|
1654
|
+
const response = await this.client.request(message);
|
|
1655
|
+
if (!response.data) return [];
|
|
1656
|
+
const entries = response.data;
|
|
1657
|
+
return entries.filter((e) => e.type === "rosterEntry");
|
|
1658
|
+
}
|
|
1611
1659
|
/**
|
|
1612
1660
|
* Get cached roster (no network request)
|
|
1613
1661
|
*/
|
|
@@ -2315,6 +2363,18 @@ var JmriClient = class extends import_index.default {
|
|
|
2315
2363
|
async searchRoster(query) {
|
|
2316
2364
|
return this.rosterManager.searchRoster(query);
|
|
2317
2365
|
}
|
|
2366
|
+
/**
|
|
2367
|
+
* Get all roster groups
|
|
2368
|
+
*/
|
|
2369
|
+
async getRosterGroups() {
|
|
2370
|
+
return this.rosterManager.getRosterGroups();
|
|
2371
|
+
}
|
|
2372
|
+
/**
|
|
2373
|
+
* Get roster entries belonging to a specific group
|
|
2374
|
+
*/
|
|
2375
|
+
async getRosterEntriesByGroup(group) {
|
|
2376
|
+
return this.rosterManager.getRosterEntriesByGroup(group);
|
|
2377
|
+
}
|
|
2318
2378
|
// ============================================================================
|
|
2319
2379
|
// Turnout Control
|
|
2320
2380
|
// ============================================================================
|
package/dist/cjs/index.js
CHANGED
|
@@ -581,8 +581,8 @@ var mockData = {
|
|
|
581
581
|
{ "name": "F4", "label": "Dynamic Brake", "lockable": true, "icon": null, "selectedIcon": null },
|
|
582
582
|
{ "name": "F5", "label": null, "lockable": false, "icon": null, "selectedIcon": null }
|
|
583
583
|
],
|
|
584
|
-
"attributes": [],
|
|
585
|
-
"rosterGroups": []
|
|
584
|
+
"attributes": [{ "name": "RosterGroup:diesels", "value": "yes" }],
|
|
585
|
+
"rosterGroups": ["diesels"]
|
|
586
586
|
},
|
|
587
587
|
"id": 1
|
|
588
588
|
},
|
|
@@ -612,8 +612,8 @@ var mockData = {
|
|
|
612
612
|
{ "name": "F3", "label": "Steam", "lockable": true, "icon": null, "selectedIcon": null },
|
|
613
613
|
{ "name": "F4", "label": null, "lockable": false, "icon": null, "selectedIcon": null }
|
|
614
614
|
],
|
|
615
|
-
"attributes": [],
|
|
616
|
-
"rosterGroups": []
|
|
615
|
+
"attributes": [{ "name": "RosterGroup:steam", "value": "yes" }],
|
|
616
|
+
"rosterGroups": ["steam"]
|
|
617
617
|
},
|
|
618
618
|
"id": 2
|
|
619
619
|
},
|
|
@@ -644,13 +644,19 @@ var mockData = {
|
|
|
644
644
|
{ "name": "F4", "label": null, "lockable": false, "icon": null, "selectedIcon": null },
|
|
645
645
|
{ "name": "F5", "label": "Mars Light", "lockable": true, "icon": null, "selectedIcon": null }
|
|
646
646
|
],
|
|
647
|
-
"attributes": [],
|
|
648
|
-
"rosterGroups": []
|
|
647
|
+
"attributes": [{ "name": "RosterGroup:diesels", "value": "yes" }],
|
|
648
|
+
"rosterGroups": ["diesels"]
|
|
649
649
|
},
|
|
650
650
|
"id": 3
|
|
651
651
|
}
|
|
652
652
|
]
|
|
653
653
|
},
|
|
654
|
+
"rosterGroup": {
|
|
655
|
+
"list": [
|
|
656
|
+
{ "type": "rosterGroup", "data": { "name": "diesels", "length": 2 } },
|
|
657
|
+
{ "type": "rosterGroup", "data": { "name": "steam", "length": 1 } }
|
|
658
|
+
]
|
|
659
|
+
},
|
|
654
660
|
"throttle": {
|
|
655
661
|
"acquire": {
|
|
656
662
|
"success": {
|
|
@@ -749,6 +755,7 @@ var mockData = {
|
|
|
749
755
|
// src/mocks/mock-response-manager.ts
|
|
750
756
|
var MockResponseManager = class {
|
|
751
757
|
constructor(options = {}) {
|
|
758
|
+
this.powerStateByPrefix = /* @__PURE__ */ new Map();
|
|
752
759
|
this.throttles = /* @__PURE__ */ new Map();
|
|
753
760
|
this.lights = /* @__PURE__ */ new Map([
|
|
754
761
|
["IL1", 4 /* OFF */],
|
|
@@ -777,6 +784,8 @@ var MockResponseManager = class {
|
|
|
777
784
|
return this.getPowerResponse(message);
|
|
778
785
|
case "roster":
|
|
779
786
|
return this.getRosterResponse(message);
|
|
787
|
+
case "rosterGroup":
|
|
788
|
+
return this.getRosterGroupResponse();
|
|
780
789
|
case "throttle":
|
|
781
790
|
return this.getThrottleResponse(message);
|
|
782
791
|
case "light":
|
|
@@ -798,34 +807,48 @@ var MockResponseManager = class {
|
|
|
798
807
|
return JSON.parse(JSON.stringify(mockData.hello));
|
|
799
808
|
}
|
|
800
809
|
/**
|
|
801
|
-
* Get power response
|
|
810
|
+
* Get power response, with optional per-prefix state tracking
|
|
802
811
|
*/
|
|
803
812
|
getPowerResponse(message) {
|
|
804
|
-
|
|
813
|
+
const prefix = message.data?.prefix;
|
|
814
|
+
if (message.method === "post" && message.data?.state !== void 0) {
|
|
815
|
+
if (prefix !== void 0) {
|
|
816
|
+
this.powerStateByPrefix.set(prefix, message.data.state);
|
|
817
|
+
return { type: "power", data: { state: message.data.state, prefix } };
|
|
818
|
+
}
|
|
805
819
|
this.powerState = message.data.state;
|
|
806
|
-
return {
|
|
807
|
-
type: "power",
|
|
808
|
-
data: { state: this.powerState }
|
|
809
|
-
};
|
|
820
|
+
return { type: "power", data: { state: this.powerState } };
|
|
810
821
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
data: { state
|
|
814
|
-
}
|
|
822
|
+
if (prefix !== void 0) {
|
|
823
|
+
const state = this.powerStateByPrefix.get(prefix) ?? 0 /* UNKNOWN */;
|
|
824
|
+
return { type: "power", data: { state, prefix } };
|
|
825
|
+
}
|
|
826
|
+
return { type: "power", data: { state: this.powerState } };
|
|
815
827
|
}
|
|
816
828
|
/**
|
|
817
|
-
* Get roster response
|
|
829
|
+
* Get roster response, optionally filtered by group
|
|
818
830
|
*/
|
|
819
831
|
getRosterResponse(message) {
|
|
820
832
|
if (message.type === "roster" && message.method === "list") {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
833
|
+
const all = JSON.parse(JSON.stringify(mockData.roster.list));
|
|
834
|
+
const group = message.params?.group;
|
|
835
|
+
if (group) {
|
|
836
|
+
return {
|
|
837
|
+
type: "roster",
|
|
838
|
+
data: all.filter((e) => e.data.rosterGroups?.includes(group))
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
return { type: "roster", data: all };
|
|
825
842
|
}
|
|
843
|
+
return { type: "roster", data: [] };
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Get roster group response
|
|
847
|
+
*/
|
|
848
|
+
getRosterGroupResponse() {
|
|
826
849
|
return {
|
|
827
|
-
type: "
|
|
828
|
-
data:
|
|
850
|
+
type: "rosterGroup",
|
|
851
|
+
data: JSON.parse(JSON.stringify(mockData.rosterGroup.list))
|
|
829
852
|
};
|
|
830
853
|
}
|
|
831
854
|
/**
|
|
@@ -844,13 +867,11 @@ var MockResponseManager = class {
|
|
|
844
867
|
F1: false,
|
|
845
868
|
F2: false,
|
|
846
869
|
F3: false,
|
|
847
|
-
F4: false
|
|
870
|
+
F4: false,
|
|
871
|
+
...data.prefix !== void 0 && { prefix: data.prefix }
|
|
848
872
|
};
|
|
849
873
|
this.throttles.set(throttleId, throttleState);
|
|
850
|
-
return {
|
|
851
|
-
type: "throttle",
|
|
852
|
-
data: { ...throttleState }
|
|
853
|
-
};
|
|
874
|
+
return { type: "throttle", data: { ...throttleState } };
|
|
854
875
|
}
|
|
855
876
|
if (data.release !== void 0 && data.throttle) {
|
|
856
877
|
this.throttles.delete(data.throttle);
|
|
@@ -983,6 +1004,7 @@ var MockResponseManager = class {
|
|
|
983
1004
|
*/
|
|
984
1005
|
reset() {
|
|
985
1006
|
this.powerState = 4 /* OFF */;
|
|
1007
|
+
this.powerStateByPrefix.clear();
|
|
986
1008
|
this.throttles.clear();
|
|
987
1009
|
this.lights = /* @__PURE__ */ new Map([
|
|
988
1010
|
["IL1", 4 /* OFF */],
|
|
@@ -1467,6 +1489,32 @@ var RosterManager = class {
|
|
|
1467
1489
|
}
|
|
1468
1490
|
return results;
|
|
1469
1491
|
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Get all roster groups
|
|
1494
|
+
*/
|
|
1495
|
+
async getRosterGroups() {
|
|
1496
|
+
const message = {
|
|
1497
|
+
type: "rosterGroup",
|
|
1498
|
+
method: "list"
|
|
1499
|
+
};
|
|
1500
|
+
const response = await this.client.request(message);
|
|
1501
|
+
if (!response.data) return [];
|
|
1502
|
+
return response.data.map((wrapper) => wrapper.data);
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Get roster entries belonging to a specific group
|
|
1506
|
+
*/
|
|
1507
|
+
async getRosterEntriesByGroup(group) {
|
|
1508
|
+
const message = {
|
|
1509
|
+
type: "roster",
|
|
1510
|
+
method: "list",
|
|
1511
|
+
params: { group }
|
|
1512
|
+
};
|
|
1513
|
+
const response = await this.client.request(message);
|
|
1514
|
+
if (!response.data) return [];
|
|
1515
|
+
const entries = response.data;
|
|
1516
|
+
return entries.filter((e) => e.type === "rosterEntry");
|
|
1517
|
+
}
|
|
1470
1518
|
/**
|
|
1471
1519
|
* Get cached roster (no network request)
|
|
1472
1520
|
*/
|
|
@@ -2179,6 +2227,18 @@ var JmriClient = class extends import_eventemitter310.EventEmitter {
|
|
|
2179
2227
|
async searchRoster(query) {
|
|
2180
2228
|
return this.rosterManager.searchRoster(query);
|
|
2181
2229
|
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Get all roster groups
|
|
2232
|
+
*/
|
|
2233
|
+
async getRosterGroups() {
|
|
2234
|
+
return this.rosterManager.getRosterGroups();
|
|
2235
|
+
}
|
|
2236
|
+
/**
|
|
2237
|
+
* Get roster entries belonging to a specific group
|
|
2238
|
+
*/
|
|
2239
|
+
async getRosterEntriesByGroup(group) {
|
|
2240
|
+
return this.rosterManager.getRosterEntriesByGroup(group);
|
|
2241
|
+
}
|
|
2182
2242
|
// ============================================================================
|
|
2183
2243
|
// Turnout Control
|
|
2184
2244
|
// ============================================================================
|
package/dist/esm/index.js
CHANGED
|
@@ -532,8 +532,8 @@ var mockData = {
|
|
|
532
532
|
{ "name": "F4", "label": "Dynamic Brake", "lockable": true, "icon": null, "selectedIcon": null },
|
|
533
533
|
{ "name": "F5", "label": null, "lockable": false, "icon": null, "selectedIcon": null }
|
|
534
534
|
],
|
|
535
|
-
"attributes": [],
|
|
536
|
-
"rosterGroups": []
|
|
535
|
+
"attributes": [{ "name": "RosterGroup:diesels", "value": "yes" }],
|
|
536
|
+
"rosterGroups": ["diesels"]
|
|
537
537
|
},
|
|
538
538
|
"id": 1
|
|
539
539
|
},
|
|
@@ -563,8 +563,8 @@ var mockData = {
|
|
|
563
563
|
{ "name": "F3", "label": "Steam", "lockable": true, "icon": null, "selectedIcon": null },
|
|
564
564
|
{ "name": "F4", "label": null, "lockable": false, "icon": null, "selectedIcon": null }
|
|
565
565
|
],
|
|
566
|
-
"attributes": [],
|
|
567
|
-
"rosterGroups": []
|
|
566
|
+
"attributes": [{ "name": "RosterGroup:steam", "value": "yes" }],
|
|
567
|
+
"rosterGroups": ["steam"]
|
|
568
568
|
},
|
|
569
569
|
"id": 2
|
|
570
570
|
},
|
|
@@ -595,13 +595,19 @@ var mockData = {
|
|
|
595
595
|
{ "name": "F4", "label": null, "lockable": false, "icon": null, "selectedIcon": null },
|
|
596
596
|
{ "name": "F5", "label": "Mars Light", "lockable": true, "icon": null, "selectedIcon": null }
|
|
597
597
|
],
|
|
598
|
-
"attributes": [],
|
|
599
|
-
"rosterGroups": []
|
|
598
|
+
"attributes": [{ "name": "RosterGroup:diesels", "value": "yes" }],
|
|
599
|
+
"rosterGroups": ["diesels"]
|
|
600
600
|
},
|
|
601
601
|
"id": 3
|
|
602
602
|
}
|
|
603
603
|
]
|
|
604
604
|
},
|
|
605
|
+
"rosterGroup": {
|
|
606
|
+
"list": [
|
|
607
|
+
{ "type": "rosterGroup", "data": { "name": "diesels", "length": 2 } },
|
|
608
|
+
{ "type": "rosterGroup", "data": { "name": "steam", "length": 1 } }
|
|
609
|
+
]
|
|
610
|
+
},
|
|
605
611
|
"throttle": {
|
|
606
612
|
"acquire": {
|
|
607
613
|
"success": {
|
|
@@ -700,6 +706,7 @@ var mockData = {
|
|
|
700
706
|
// src/mocks/mock-response-manager.ts
|
|
701
707
|
var MockResponseManager = class {
|
|
702
708
|
constructor(options = {}) {
|
|
709
|
+
this.powerStateByPrefix = /* @__PURE__ */ new Map();
|
|
703
710
|
this.throttles = /* @__PURE__ */ new Map();
|
|
704
711
|
this.lights = /* @__PURE__ */ new Map([
|
|
705
712
|
["IL1", 4 /* OFF */],
|
|
@@ -728,6 +735,8 @@ var MockResponseManager = class {
|
|
|
728
735
|
return this.getPowerResponse(message);
|
|
729
736
|
case "roster":
|
|
730
737
|
return this.getRosterResponse(message);
|
|
738
|
+
case "rosterGroup":
|
|
739
|
+
return this.getRosterGroupResponse();
|
|
731
740
|
case "throttle":
|
|
732
741
|
return this.getThrottleResponse(message);
|
|
733
742
|
case "light":
|
|
@@ -749,34 +758,48 @@ var MockResponseManager = class {
|
|
|
749
758
|
return JSON.parse(JSON.stringify(mockData.hello));
|
|
750
759
|
}
|
|
751
760
|
/**
|
|
752
|
-
* Get power response
|
|
761
|
+
* Get power response, with optional per-prefix state tracking
|
|
753
762
|
*/
|
|
754
763
|
getPowerResponse(message) {
|
|
755
|
-
|
|
764
|
+
const prefix = message.data?.prefix;
|
|
765
|
+
if (message.method === "post" && message.data?.state !== void 0) {
|
|
766
|
+
if (prefix !== void 0) {
|
|
767
|
+
this.powerStateByPrefix.set(prefix, message.data.state);
|
|
768
|
+
return { type: "power", data: { state: message.data.state, prefix } };
|
|
769
|
+
}
|
|
756
770
|
this.powerState = message.data.state;
|
|
757
|
-
return {
|
|
758
|
-
type: "power",
|
|
759
|
-
data: { state: this.powerState }
|
|
760
|
-
};
|
|
771
|
+
return { type: "power", data: { state: this.powerState } };
|
|
761
772
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
data: { state
|
|
765
|
-
}
|
|
773
|
+
if (prefix !== void 0) {
|
|
774
|
+
const state = this.powerStateByPrefix.get(prefix) ?? 0 /* UNKNOWN */;
|
|
775
|
+
return { type: "power", data: { state, prefix } };
|
|
776
|
+
}
|
|
777
|
+
return { type: "power", data: { state: this.powerState } };
|
|
766
778
|
}
|
|
767
779
|
/**
|
|
768
|
-
* Get roster response
|
|
780
|
+
* Get roster response, optionally filtered by group
|
|
769
781
|
*/
|
|
770
782
|
getRosterResponse(message) {
|
|
771
783
|
if (message.type === "roster" && message.method === "list") {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
784
|
+
const all = JSON.parse(JSON.stringify(mockData.roster.list));
|
|
785
|
+
const group = message.params?.group;
|
|
786
|
+
if (group) {
|
|
787
|
+
return {
|
|
788
|
+
type: "roster",
|
|
789
|
+
data: all.filter((e) => e.data.rosterGroups?.includes(group))
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
return { type: "roster", data: all };
|
|
776
793
|
}
|
|
794
|
+
return { type: "roster", data: [] };
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Get roster group response
|
|
798
|
+
*/
|
|
799
|
+
getRosterGroupResponse() {
|
|
777
800
|
return {
|
|
778
|
-
type: "
|
|
779
|
-
data:
|
|
801
|
+
type: "rosterGroup",
|
|
802
|
+
data: JSON.parse(JSON.stringify(mockData.rosterGroup.list))
|
|
780
803
|
};
|
|
781
804
|
}
|
|
782
805
|
/**
|
|
@@ -795,13 +818,11 @@ var MockResponseManager = class {
|
|
|
795
818
|
F1: false,
|
|
796
819
|
F2: false,
|
|
797
820
|
F3: false,
|
|
798
|
-
F4: false
|
|
821
|
+
F4: false,
|
|
822
|
+
...data.prefix !== void 0 && { prefix: data.prefix }
|
|
799
823
|
};
|
|
800
824
|
this.throttles.set(throttleId, throttleState);
|
|
801
|
-
return {
|
|
802
|
-
type: "throttle",
|
|
803
|
-
data: { ...throttleState }
|
|
804
|
-
};
|
|
825
|
+
return { type: "throttle", data: { ...throttleState } };
|
|
805
826
|
}
|
|
806
827
|
if (data.release !== void 0 && data.throttle) {
|
|
807
828
|
this.throttles.delete(data.throttle);
|
|
@@ -934,6 +955,7 @@ var MockResponseManager = class {
|
|
|
934
955
|
*/
|
|
935
956
|
reset() {
|
|
936
957
|
this.powerState = 4 /* OFF */;
|
|
958
|
+
this.powerStateByPrefix.clear();
|
|
937
959
|
this.throttles.clear();
|
|
938
960
|
this.lights = /* @__PURE__ */ new Map([
|
|
939
961
|
["IL1", 4 /* OFF */],
|
|
@@ -1418,6 +1440,32 @@ var RosterManager = class {
|
|
|
1418
1440
|
}
|
|
1419
1441
|
return results;
|
|
1420
1442
|
}
|
|
1443
|
+
/**
|
|
1444
|
+
* Get all roster groups
|
|
1445
|
+
*/
|
|
1446
|
+
async getRosterGroups() {
|
|
1447
|
+
const message = {
|
|
1448
|
+
type: "rosterGroup",
|
|
1449
|
+
method: "list"
|
|
1450
|
+
};
|
|
1451
|
+
const response = await this.client.request(message);
|
|
1452
|
+
if (!response.data) return [];
|
|
1453
|
+
return response.data.map((wrapper) => wrapper.data);
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Get roster entries belonging to a specific group
|
|
1457
|
+
*/
|
|
1458
|
+
async getRosterEntriesByGroup(group) {
|
|
1459
|
+
const message = {
|
|
1460
|
+
type: "roster",
|
|
1461
|
+
method: "list",
|
|
1462
|
+
params: { group }
|
|
1463
|
+
};
|
|
1464
|
+
const response = await this.client.request(message);
|
|
1465
|
+
if (!response.data) return [];
|
|
1466
|
+
const entries = response.data;
|
|
1467
|
+
return entries.filter((e) => e.type === "rosterEntry");
|
|
1468
|
+
}
|
|
1421
1469
|
/**
|
|
1422
1470
|
* Get cached roster (no network request)
|
|
1423
1471
|
*/
|
|
@@ -2130,6 +2178,18 @@ var JmriClient = class extends EventEmitter10 {
|
|
|
2130
2178
|
async searchRoster(query) {
|
|
2131
2179
|
return this.rosterManager.searchRoster(query);
|
|
2132
2180
|
}
|
|
2181
|
+
/**
|
|
2182
|
+
* Get all roster groups
|
|
2183
|
+
*/
|
|
2184
|
+
async getRosterGroups() {
|
|
2185
|
+
return this.rosterManager.getRosterGroups();
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* Get roster entries belonging to a specific group
|
|
2189
|
+
*/
|
|
2190
|
+
async getRosterEntriesByGroup(group) {
|
|
2191
|
+
return this.rosterManager.getRosterEntriesByGroup(group);
|
|
2192
|
+
}
|
|
2133
2193
|
// ============================================================================
|
|
2134
2194
|
// Turnout Control
|
|
2135
2195
|
// ============================================================================
|
package/dist/types/client.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { EventEmitter } from 'eventemitter3';
|
|
5
5
|
import { WebSocketClient } from './core/websocket-client.js';
|
|
6
6
|
import { PartialClientOptions } from './types/client-options.js';
|
|
7
|
-
import { PowerState, RosterEntryWrapper, TurnoutState, TurnoutData, LightState, LightData, SystemConnectionData } from './types/jmri-messages.js';
|
|
7
|
+
import { PowerState, RosterEntryWrapper, RosterGroup, TurnoutState, TurnoutData, LightState, LightData, SystemConnectionData } from './types/jmri-messages.js';
|
|
8
8
|
import { ConnectionState } from './types/events.js';
|
|
9
9
|
import { ThrottleAcquireOptions, ThrottleFunctionKey, ThrottleState } from './types/throttle.js';
|
|
10
10
|
/**
|
|
@@ -104,6 +104,14 @@ export declare class JmriClient extends EventEmitter {
|
|
|
104
104
|
* Search roster by partial name match
|
|
105
105
|
*/
|
|
106
106
|
searchRoster(query: string): Promise<RosterEntryWrapper[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Get all roster groups
|
|
109
|
+
*/
|
|
110
|
+
getRosterGroups(): Promise<RosterGroup[]>;
|
|
111
|
+
/**
|
|
112
|
+
* Get roster entries belonging to a specific group
|
|
113
|
+
*/
|
|
114
|
+
getRosterEntriesByGroup(group: string): Promise<RosterEntryWrapper[]>;
|
|
107
115
|
/**
|
|
108
116
|
* Get the current state of a turnout
|
|
109
117
|
*/
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { JmriClient } from './client.js';
|
|
6
6
|
export { WebSocketClient } from './core/websocket-client.js';
|
|
7
|
-
export { JmriClientOptions, PartialClientOptions, ReconnectionOptions, HeartbeatOptions, MockOptions, PowerState, TurnoutState, LightState, RosterEntry, TurnoutData, LightData, JmriMessage, PowerMessage, TurnoutMessage, LightMessage, ThrottleMessage, RosterMessage, ConnectionState, EventPayloads, ThrottleAcquireOptions, ThrottleFunctionKey, ThrottleState } from './types/index.js';
|
|
7
|
+
export { JmriClientOptions, PartialClientOptions, ReconnectionOptions, HeartbeatOptions, MockOptions, PowerState, TurnoutState, LightState, RosterEntry, RosterGroup, TurnoutData, LightData, JmriMessage, PowerMessage, TurnoutMessage, LightMessage, ThrottleMessage, RosterMessage, ConnectionState, EventPayloads, ThrottleAcquireOptions, ThrottleFunctionKey, ThrottleState } from './types/index.js';
|
|
8
8
|
export { isThrottleFunctionKey, isValidSpeed } from './types/throttle.js';
|
|
9
9
|
export { powerStateToString, turnoutStateToString, lightStateToString } from './types/jmri-messages.js';
|
|
10
10
|
export { MockResponseManager, mockResponseManager, mockData } from './mocks/index.js';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Roster management
|
|
3
3
|
*/
|
|
4
4
|
import { WebSocketClient } from '../core/websocket-client.js';
|
|
5
|
-
import { RosterEntryWrapper } from '../types/jmri-messages.js';
|
|
5
|
+
import { RosterEntryWrapper, RosterGroup } from '../types/jmri-messages.js';
|
|
6
6
|
/**
|
|
7
7
|
* Manages locomotive roster
|
|
8
8
|
*/
|
|
@@ -26,6 +26,14 @@ export declare class RosterManager {
|
|
|
26
26
|
* Search roster by partial name match
|
|
27
27
|
*/
|
|
28
28
|
searchRoster(query: string): Promise<RosterEntryWrapper[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Get all roster groups
|
|
31
|
+
*/
|
|
32
|
+
getRosterGroups(): Promise<RosterGroup[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Get roster entries belonging to a specific group
|
|
35
|
+
*/
|
|
36
|
+
getRosterEntriesByGroup(group: string): Promise<RosterEntryWrapper[]>;
|
|
29
37
|
/**
|
|
30
38
|
* Get cached roster (no network request)
|
|
31
39
|
*/
|
|
@@ -96,8 +96,11 @@ export declare const mockData: {
|
|
|
96
96
|
readonly icon: null;
|
|
97
97
|
readonly selectedIcon: null;
|
|
98
98
|
}];
|
|
99
|
-
readonly attributes: readonly [
|
|
100
|
-
|
|
99
|
+
readonly attributes: readonly [{
|
|
100
|
+
readonly name: "RosterGroup:diesels";
|
|
101
|
+
readonly value: "yes";
|
|
102
|
+
}];
|
|
103
|
+
readonly rosterGroups: readonly ["diesels"];
|
|
101
104
|
};
|
|
102
105
|
readonly id: 1;
|
|
103
106
|
}, {
|
|
@@ -150,8 +153,11 @@ export declare const mockData: {
|
|
|
150
153
|
readonly icon: null;
|
|
151
154
|
readonly selectedIcon: null;
|
|
152
155
|
}];
|
|
153
|
-
readonly attributes: readonly [
|
|
154
|
-
|
|
156
|
+
readonly attributes: readonly [{
|
|
157
|
+
readonly name: "RosterGroup:steam";
|
|
158
|
+
readonly value: "yes";
|
|
159
|
+
}];
|
|
160
|
+
readonly rosterGroups: readonly ["steam"];
|
|
155
161
|
};
|
|
156
162
|
readonly id: 2;
|
|
157
163
|
}, {
|
|
@@ -210,12 +216,30 @@ export declare const mockData: {
|
|
|
210
216
|
readonly icon: null;
|
|
211
217
|
readonly selectedIcon: null;
|
|
212
218
|
}];
|
|
213
|
-
readonly attributes: readonly [
|
|
214
|
-
|
|
219
|
+
readonly attributes: readonly [{
|
|
220
|
+
readonly name: "RosterGroup:diesels";
|
|
221
|
+
readonly value: "yes";
|
|
222
|
+
}];
|
|
223
|
+
readonly rosterGroups: readonly ["diesels"];
|
|
215
224
|
};
|
|
216
225
|
readonly id: 3;
|
|
217
226
|
}];
|
|
218
227
|
};
|
|
228
|
+
readonly rosterGroup: {
|
|
229
|
+
readonly list: readonly [{
|
|
230
|
+
readonly type: "rosterGroup";
|
|
231
|
+
readonly data: {
|
|
232
|
+
readonly name: "diesels";
|
|
233
|
+
readonly length: 2;
|
|
234
|
+
};
|
|
235
|
+
}, {
|
|
236
|
+
readonly type: "rosterGroup";
|
|
237
|
+
readonly data: {
|
|
238
|
+
readonly name: "steam";
|
|
239
|
+
readonly length: 1;
|
|
240
|
+
};
|
|
241
|
+
}];
|
|
242
|
+
};
|
|
219
243
|
readonly throttle: {
|
|
220
244
|
readonly acquire: {
|
|
221
245
|
readonly success: {
|
|
@@ -20,6 +20,7 @@ export interface MockResponseManagerOptions {
|
|
|
20
20
|
export declare class MockResponseManager {
|
|
21
21
|
private responseDelay;
|
|
22
22
|
private powerState;
|
|
23
|
+
private powerStateByPrefix;
|
|
23
24
|
private throttles;
|
|
24
25
|
private lights;
|
|
25
26
|
private turnouts;
|
|
@@ -33,13 +34,17 @@ export declare class MockResponseManager {
|
|
|
33
34
|
*/
|
|
34
35
|
private getHelloResponse;
|
|
35
36
|
/**
|
|
36
|
-
* Get power response
|
|
37
|
+
* Get power response, with optional per-prefix state tracking
|
|
37
38
|
*/
|
|
38
39
|
private getPowerResponse;
|
|
39
40
|
/**
|
|
40
|
-
* Get roster response
|
|
41
|
+
* Get roster response, optionally filtered by group
|
|
41
42
|
*/
|
|
42
43
|
private getRosterResponse;
|
|
44
|
+
/**
|
|
45
|
+
* Get roster group response
|
|
46
|
+
*/
|
|
47
|
+
private getRosterGroupResponse;
|
|
43
48
|
/**
|
|
44
49
|
* Get throttle response
|
|
45
50
|
*/
|
|
@@ -10,6 +10,7 @@ export interface JmriMessage {
|
|
|
10
10
|
method?: 'get' | 'post' | 'put' | 'delete' | 'list';
|
|
11
11
|
data?: any;
|
|
12
12
|
id?: number;
|
|
13
|
+
params?: Record<string, string>;
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Power state values (from JMRI JSON protocol constants)
|
|
@@ -166,6 +167,27 @@ export interface RosterMessage extends JmriMessage {
|
|
|
166
167
|
export interface RosterData {
|
|
167
168
|
[key: string]: RosterEntry;
|
|
168
169
|
}
|
|
170
|
+
export interface RosterGroup {
|
|
171
|
+
name: string;
|
|
172
|
+
length: number;
|
|
173
|
+
}
|
|
174
|
+
export interface RosterGroupWrapper {
|
|
175
|
+
type: 'rosterGroup';
|
|
176
|
+
data: RosterGroup;
|
|
177
|
+
}
|
|
178
|
+
export interface RosterGroupMessage extends JmriMessage {
|
|
179
|
+
type: 'rosterGroup';
|
|
180
|
+
method: 'list';
|
|
181
|
+
data?: RosterGroupWrapper[];
|
|
182
|
+
}
|
|
183
|
+
export interface RosterMessageWithParams extends JmriMessage {
|
|
184
|
+
type: 'roster';
|
|
185
|
+
method: 'list';
|
|
186
|
+
params?: {
|
|
187
|
+
group: string;
|
|
188
|
+
};
|
|
189
|
+
data?: RosterResponse;
|
|
190
|
+
}
|
|
169
191
|
/**
|
|
170
192
|
* Turnout state values (from JMRI JSON protocol constants)
|
|
171
193
|
* UNKNOWN = 0 (state cannot be determined)
|
package/docs/API.md
CHANGED
|
@@ -141,6 +141,13 @@ const entry = await client.getRosterEntryByAddress(3);
|
|
|
141
141
|
|
|
142
142
|
// Search roster
|
|
143
143
|
const results = await client.searchRoster('steam');
|
|
144
|
+
|
|
145
|
+
// Get all roster groups
|
|
146
|
+
const groups: RosterGroup[] = await client.getRosterGroups();
|
|
147
|
+
// [{ name: 'locos', length: 3 }, { name: 'trams', length: 2 }]
|
|
148
|
+
|
|
149
|
+
// Get roster entries for a specific group
|
|
150
|
+
const locos = await client.getRosterEntriesByGroup('locos');
|
|
144
151
|
```
|
|
145
152
|
|
|
146
153
|
## Light Control
|
|
@@ -267,6 +274,7 @@ import {
|
|
|
267
274
|
LightState,
|
|
268
275
|
LightData,
|
|
269
276
|
RosterEntry,
|
|
277
|
+
RosterGroup,
|
|
270
278
|
TurnoutState,
|
|
271
279
|
TurnoutData,
|
|
272
280
|
ThrottleState,
|
package/docs/MOCK_MODE.md
CHANGED
|
@@ -65,13 +65,18 @@ Connection establishment response with JMRI version info:
|
|
|
65
65
|
Track power ON/OFF states
|
|
66
66
|
|
|
67
67
|
### Roster
|
|
68
|
-
Three sample locomotives:
|
|
69
|
-
- **CSX754** - GP38-2 diesel locomotive
|
|
70
|
-
- **UP3985** - Challenger 4-6-6-4 steam locomotive
|
|
71
|
-
- **BNSF5240** - SD40-2 diesel locomotive
|
|
68
|
+
Three sample locomotives across two groups:
|
|
69
|
+
- **CSX754** - GP38-2 diesel locomotive (`diesels` group)
|
|
70
|
+
- **UP3985** - Challenger 4-6-6-4 steam locomotive (`steam` group)
|
|
71
|
+
- **BNSF5240** - SD40-2 diesel locomotive (`diesels` group)
|
|
72
72
|
|
|
73
73
|
Each includes realistic function key mappings (F0-F4).
|
|
74
74
|
|
|
75
|
+
### Roster Groups
|
|
76
|
+
Two sample groups:
|
|
77
|
+
- **diesels** - CSX754, BNSF5240 (length: 2)
|
|
78
|
+
- **steam** - UP3985 (length: 1)
|
|
79
|
+
|
|
75
80
|
### Lights
|
|
76
81
|
Three sample lights:
|
|
77
82
|
- **IL1** - Yard Light (OFF)
|
|
@@ -163,10 +168,10 @@ const response = await mockManager.getMockResponse({
|
|
|
163
168
|
|
|
164
169
|
The mock system maintains state for realistic behavior:
|
|
165
170
|
|
|
166
|
-
- **Power state** - Remembers if power is ON or OFF
|
|
171
|
+
- **Power state** - Remembers if power is ON or OFF; tracked independently per connection prefix when one is supplied
|
|
167
172
|
- **Light states** - Tracks ON/OFF state per light
|
|
168
173
|
- **Turnout states** - Tracks CLOSED/THROWN state per turnout
|
|
169
|
-
- **Throttles** - Tracks acquired throttles and their states
|
|
174
|
+
- **Throttles** - Tracks acquired throttles and their states, including connection prefix
|
|
170
175
|
- **Speed/Direction** - Maintains current speed and direction per throttle
|
|
171
176
|
- **Functions** - Tracks function key states (F0-F28)
|
|
172
177
|
|
|
@@ -208,11 +213,12 @@ The mock data structure follows the JMRI JSON protocol specification.
|
|
|
208
213
|
Mock mode implements the full JMRI client API. All methods work identically:
|
|
209
214
|
|
|
210
215
|
- ✅ `connect()` / `disconnect()`
|
|
211
|
-
- ✅ `getPower()` / `powerOn()` / `powerOff()`
|
|
212
|
-
- ✅ `getRoster()`
|
|
216
|
+
- ✅ `getPower()` / `powerOn()` / `powerOff()` (with optional prefix)
|
|
217
|
+
- ✅ `getRoster()` / `getRosterEntryByName()` / `getRosterEntryByAddress()` / `searchRoster()`
|
|
218
|
+
- ✅ `getRosterGroups()` / `getRosterEntriesByGroup()`
|
|
213
219
|
- ✅ `getLight()` / `setLight()` / `turnOnLight()` / `turnOffLight()` / `listLights()`
|
|
214
220
|
- ✅ `getTurnout()` / `setTurnout()` / `throwTurnout()` / `closeTurnout()` / `listTurnouts()`
|
|
215
|
-
- ✅ `acquireThrottle()` / `releaseThrottle()`
|
|
221
|
+
- ✅ `acquireThrottle()` / `releaseThrottle()` (with optional prefix)
|
|
216
222
|
- ✅ `setThrottleSpeed()`
|
|
217
223
|
- ✅ `setThrottleDirection()`
|
|
218
224
|
- ✅ `setThrottleFunction()`
|
package/package.json
CHANGED