├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── Train-API │ ├── Debug Data │ ├── ice.json │ ├── paris.json │ ├── sbahnStuttgart.json │ ├── status.json │ ├── tripInfo1.json │ └── tripInfo2.json │ ├── ICE Baureihen │ ├── BR401.pdf │ ├── BR402.pdf │ ├── BR403 Serie 1.pdf │ ├── BR403 Serie 2.pdf │ ├── BR406.pdf │ ├── BR407.pdf │ ├── BR411 Serie 1.pdf │ ├── BR411 Serie 2.pdf │ ├── BR412.pdf │ └── BR415.pdf │ ├── ICEConnection.swift │ ├── InternetConnection.swift │ ├── JourneyStop.swift │ ├── Media.xcassets │ ├── BR401.imageset │ │ ├── Contents.json │ │ └── ice 1.png │ ├── BR402.imageset │ │ ├── Contents.json │ │ └── ice 2.png │ ├── BR403.imageset │ │ ├── BR403.png │ │ └── Contents.json │ ├── BR406.imageset │ │ ├── BR406.png │ │ └── Contents.json │ ├── BR407.imageset │ │ ├── BR407.png │ │ └── Contents.json │ ├── BR411.imageset │ │ ├── BR411.png │ │ └── Contents.json │ ├── BR412.imageset │ │ ├── Contents.json │ │ └── ice 4.png │ ├── BR415.imageset │ │ ├── BR415.png │ │ └── Contents.json │ └── Contents.json │ ├── TrainMetaData.swift │ ├── TrainType.swift │ └── TripData.swift └── Tests └── Train-APITests └── Train_APITests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "alamofire", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/Alamofire/Alamofire", 7 | "state" : { 8 | "revision" : "354dda32d89fc8cd4f5c46487f64957d355f53d8", 9 | "version" : "5.6.1" 10 | } 11 | }, 12 | { 13 | "identity" : "fredkit", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/frogg/FredKit", 16 | "state" : { 17 | "revision" : "bb465a18b47ed01c8487e08c3a106e89e6ad0b81", 18 | "version" : "0.1.42" 19 | } 20 | } 21 | ], 22 | "version" : 2 23 | } 24 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.6 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Train-API-Swift-Package", 8 | platforms: [ 9 | .macOS(.v10_12), 10 | .iOS(.v12) 11 | ], 12 | products: [ 13 | // Products define the executables and libraries a package produces, and make them visible to other packages. 14 | .library( 15 | name: "Train-API-Swift-Package", 16 | targets: ["Train-API"]), 17 | ], 18 | dependencies: [ 19 | // Dependencies declare other packages that this package depends on. 20 | .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"), 21 | .package(url: "https://github.com/frogg/FredKit", from: "0.1.0") 22 | ], 23 | targets: [ 24 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 25 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 26 | .target( 27 | name: "Train-API", 28 | dependencies: [ 29 | "Alamofire", 30 | "FredKit" 31 | ], 32 | resources: [ 33 | .process("Debug Data/ice.json"), 34 | .process("Debug Data/paris.json") 35 | ] 36 | ), 37 | .testTarget( 38 | name: "Train-APITests", 39 | dependencies: ["Train-API"]), 40 | ] 41 | ) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Train-API 2 | 3 | Offering a simple Swift API for real-time tain/travel-information. 4 | 5 | ## ICE API 6 | This Swift Package has been developed to power [ICE Buddy](https://ice-buddy.riedel.wtf). 7 | 8 | ### On-Board Server 9 | This Swift Package connects to the on-board server of ICE trains, so connecting to the WiFi@DB or WiFionICE is mandatory. 10 | 11 | Then, REST-Requests are sent to the following endpoints: 12 | - https://iceportal.de/api1/rs/status 13 | - https://iceportal.de/api1/rs/tripInfo/trip 14 | 15 | ### Available Data Points 16 | - Current speed (km/h) 17 | - Train name and journey title (e.g., ICE 643: Düsseldorf Hbf -> Berlin Ostbahnhof) 18 | - Train model (e.g., ICE 4) 19 | - Train model image 20 | - Journey (all train stations), incluing: 21 | - Track 22 | - Planned arrival time 23 | - Actual arrival time (delay) 24 | - Current internet quality (stable / unstable) 25 | 26 | 27 | 28 | ## TGV, ÖBB, SBB CFF FFS? 29 | We encourage all thrain enthusiasts from all over the world to contribute to this open-source project! 30 | -------------------------------------------------------------------------------- /Sources/Train-API/Debug Data/ice.json: -------------------------------------------------------------------------------- 1 | {"connection":true,"serviceLevel":"AVAILABLE_SERVICE","gpsStatus":"VALID","internet":"HIGH","latitude":49.2987785,"longitude":11.361207,"tileY":-78,"tileX":125,"series":"411","serverTime":1647438292926,"speed":120.0,"trainType":"ICE","tzn":"ICE0334","wagonClass":"SECOND","connectivity":{"currentState":"UNSTABLE","nextState":"HIGH","remainingTimeSeconds":1200},"bapInstalled":true} 2 | -------------------------------------------------------------------------------- /Sources/Train-API/Debug Data/paris.json: -------------------------------------------------------------------------------- 1 | {"trip":{"tripDate":"2022-03-23","trainType":"ICE","vzn":"9557","actualPosition":0,"distanceFromLastStop":0,"totalDistance":524136,"stopInfo":{"scheduledNext":"8700011_00","actualNext":"8700011_00","actualLast":"","actualLastStarted":"","finalStationName":"Frankfurt (Main) Hbf","finalStationEvaNr":"8000105_00"},"stops":[{"station":{"evaNr":"8700011_00","name":"Paris Est","code":null,"geocoordinates":{"latitude":48.876976,"longitude":2.35912}},"timetable":{"scheduledArrivalTime":null,"actualArrivalTime":null,"showActualArrivalTime":null,"arrivalDelay":"","scheduledDepartureTime":1648058640000,"actualDepartureTime":null,"showActualDepartureTime":false,"departureDelay":""},"track":{"scheduled":null,"actual":""},"info":{"status":0,"passed":true,"positionStatus":"arrived","distance":0,"distanceFromStart":0},"delayReasons":null},{"station":{"evaNr":"8700271_00","name":"Forbach (F)","code":null,"geocoordinates":{"latitude":49.189217,"longitude":6.900258}},"timetable":{"scheduledArrivalTime":1648064820000,"actualArrivalTime":null,"showActualArrivalTime":false,"arrivalDelay":"","scheduledDepartureTime":1648064880000,"actualDepartureTime":null,"showActualDepartureTime":false,"departureDelay":""},"track":{"scheduled":null,"actual":""},"info":{"status":0,"passed":false,"positionStatus":"future","distance":332916,"distanceFromStart":332916},"delayReasons":null},{"station":{"evaNr":"8000323_00","name":"Saarbrücken Hbf","code":null,"geocoordinates":{"latitude":49.241065,"longitude":6.991021}},"timetable":{"scheduledArrivalTime":1648065420000,"actualArrivalTime":null,"showActualArrivalTime":false,"arrivalDelay":"","scheduledDepartureTime":1648065540000,"actualDepartureTime":null,"showActualDepartureTime":false,"departureDelay":""},"track":{"scheduled":"5","actual":"5"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":8760,"distanceFromStart":341676},"delayReasons":null},{"station":{"evaNr":"8000189_00","name":"Kaiserslautern Hbf","code":null,"geocoordinates":{"latitude":49.436137,"longitude":7.768716}},"timetable":{"scheduledArrivalTime":1648067700000,"actualArrivalTime":null,"showActualArrivalTime":false,"arrivalDelay":"","scheduledDepartureTime":1648067820000,"actualDepartureTime":null,"showActualDepartureTime":false,"departureDelay":""},"track":{"scheduled":"4","actual":"4"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":60394,"distanceFromStart":402070},"delayReasons":null},{"station":{"evaNr":"8000244_00","name":"Mannheim Hbf","code":null,"geocoordinates":{"latitude":49.479354,"longitude":8.468921}},"timetable":{"scheduledArrivalTime":1648070220000,"actualArrivalTime":null,"showActualArrivalTime":false,"arrivalDelay":"","scheduledDepartureTime":1648070340000,"actualDepartureTime":null,"showActualDepartureTime":false,"departureDelay":""},"track":{"scheduled":"3","actual":"3"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":50851,"distanceFromStart":452921},"delayReasons":null},{"station":{"evaNr":"8000105_00","name":"Frankfurt (Main) Hbf","code":null,"geocoordinates":{"latitude":50.107145,"longitude":8.663789}},"timetable":{"scheduledArrivalTime":1648072860000,"actualArrivalTime":null,"showActualArrivalTime":false,"arrivalDelay":"","scheduledDepartureTime":null,"actualDepartureTime":null,"showActualDepartureTime":null,"departureDelay":""},"track":{"scheduled":"18","actual":"18"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":71215,"distanceFromStart":524136},"delayReasons":null}]},"connection":null,"selectedRoute":{"conflictInfo":{"status":"NO_CONFLICT","text":null},"mobility":null},"active":null} -------------------------------------------------------------------------------- /Sources/Train-API/Debug Data/sbahnStuttgart.json: -------------------------------------------------------------------------------- 1 | {"journeyId":"a513f057-5b20-493f-b3a5-7b2ab2738144","administrationId":"800643","tenantId":"badenwuerttemberg","name":"S 2","no":7855,"category":"S","date":"2021-12-22","stops":[{"station":{"evaNo":"8005424","name":"Schorndorf"},"status":"Normal","track":{"target":"1","prediction":"1"},"departureTime":{"target":"2021-12-22T18:03:00.000Z","predicted":"2021-12-22T18:03:23.000Z","predictedTimeInMs":1640196203000,"targetTimeInMs":1640196180000,"timeType":"REAL","diff":0},"messages":[]},{"station":{"evaNo":"8006277","name":"Weiler(Rems)"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"f65b0923-60ee-4d91-b962-9fa88f92686e","arrivalTime":{"target":"2021-12-22T18:06:00.000Z","predicted":"2021-12-22T18:06:00.000Z","predictedTimeInMs":1640196360000,"targetTimeInMs":1640196360000,"timeType":"REAL","diff":0},"messages":[],"departureTime":{"target":"2021-12-22T18:06:00.000Z","predicted":"2021-12-22T18:06:28.000Z","predictedTimeInMs":1640196388000,"targetTimeInMs":1640196360000,"timeType":"REAL","diff":0}},{"station":{"evaNo":"8006485","name":"Winterbach(Schorndorf)"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"4cf1cb9c-b99a-4555-ad7c-a79fcbb91e0b","arrivalTime":{"target":"2021-12-22T18:08:00.000Z","predicted":"2021-12-22T18:08:08.000Z","predictedTimeInMs":1640196488000,"targetTimeInMs":1640196480000,"timeType":"REAL","diff":0},"messages":[],"departureTime":{"target":"2021-12-22T18:08:00.000Z","predicted":"2021-12-22T18:08:34.000Z","predictedTimeInMs":1640196514000,"targetTimeInMs":1640196480000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8002240","name":"Geradstetten"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"b7e65e58-1764-4ac8-81e8-7bc6cb63ac71","arrivalTime":{"target":"2021-12-22T18:10:00.000Z","predicted":"2021-12-22T18:11:09.000Z","predictedTimeInMs":1640196669000,"targetTimeInMs":1640196600000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:11:00.000Z","predicted":"2021-12-22T18:11:46.000Z","predictedTimeInMs":1640196706000,"targetTimeInMs":1640196660000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8002448","name":"Grunbach"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"c5a1fecc-eeda-45f7-a34c-cbddbdb535f2","arrivalTime":{"target":"2021-12-22T18:12:00.000Z","predicted":"2021-12-22T18:13:15.000Z","predictedTimeInMs":1640196795000,"targetTimeInMs":1640196720000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:13:00.000Z","predicted":"2021-12-22T18:13:48.000Z","predictedTimeInMs":1640196828000,"targetTimeInMs":1640196780000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8000934","name":"Beutelsbach"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"c82d7766-ea19-439e-ad98-8d7445a7f7e7","arrivalTime":{"target":"2021-12-22T18:15:00.000Z","predicted":"2021-12-22T18:15:53.000Z","predictedTimeInMs":1640196953000,"targetTimeInMs":1640196900000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:15:00.000Z","predicted":"2021-12-22T18:16:21.000Z","predictedTimeInMs":1640196981000,"targetTimeInMs":1640196900000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8001785","name":"Endersbach"},"status":"Normal","track":{"target":"3","prediction":"3"},"arrivalId":"04d84c4b-30b5-4769-83e0-ed2a0c37fb95","arrivalTime":{"target":"2021-12-22T18:17:00.000Z","predicted":"2021-12-22T18:17:37.000Z","predictedTimeInMs":1640197057000,"targetTimeInMs":1640197020000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:17:00.000Z","predicted":"2021-12-22T18:18:08.000Z","predictedTimeInMs":1640197088000,"targetTimeInMs":1640197020000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8005726","name":"Stetten-Beinstein"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"b06a157f-f958-4dc9-899f-1aaa36e8f000","arrivalTime":{"target":"2021-12-22T18:19:00.000Z","predicted":"2021-12-22T18:19:29.000Z","predictedTimeInMs":1640197169000,"targetTimeInMs":1640197140000,"timeType":"REAL","diff":0},"messages":[],"departureTime":{"target":"2021-12-22T18:19:00.000Z","predicted":"2021-12-22T18:20:07.000Z","predictedTimeInMs":1640197207000,"targetTimeInMs":1640197140000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8005157","name":"Rommelshausen"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"e5440928-7610-4316-8969-61110a56ddf8","arrivalTime":{"target":"2021-12-22T18:21:00.000Z","predicted":"2021-12-22T18:22:25.000Z","predictedTimeInMs":1640197345000,"targetTimeInMs":1640197260000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:22:00.000Z","predicted":"2021-12-22T18:23:23.000Z","predictedTimeInMs":1640197403000,"targetTimeInMs":1640197320000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8000180","name":"Waiblingen"},"status":"Normal","track":{"target":"5","prediction":"5"},"arrivalId":"9675173f-28eb-44bd-8f5c-323b1f9e2218","arrivalTime":{"target":"2021-12-22T18:24:00.000Z","predicted":"2021-12-22T18:25:18.000Z","predictedTimeInMs":1640197518000,"targetTimeInMs":1640197440000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:25:00.000Z","predicted":"2021-12-22T18:25:33.000Z","predictedTimeInMs":1640197533000,"targetTimeInMs":1640197500000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8001974","name":"Fellbach"},"status":"Normal","track":{"target":"4","prediction":"4"},"arrivalId":"5f8deb4a-26f2-4ab4-b036-c94d05ae2618","arrivalTime":{"target":"2021-12-22T18:27:00.000Z","predicted":"2021-12-22T18:27:46.000Z","predictedTimeInMs":1640197666000,"targetTimeInMs":1640197620000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:28:00.000Z","predicted":"2021-12-22T18:28:16.000Z","predictedTimeInMs":1640197696000,"targetTimeInMs":1640197680000,"timeType":"REAL","diff":0}},{"station":{"evaNo":"8005774","name":"Stuttgart-Sommerrain"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"a51e7615-3319-408f-814d-7bb90dd3366f","arrivalTime":{"target":"2021-12-22T18:30:00.000Z","predicted":"2021-12-22T18:29:58.000Z","predictedTimeInMs":1640197798000,"targetTimeInMs":1640197800000,"timeType":"REAL","diff":0},"messages":[],"departureTime":{"target":"2021-12-22T18:30:00.000Z","predicted":"2021-12-22T18:30:59.000Z","predictedTimeInMs":1640197859000,"targetTimeInMs":1640197800000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8004357","name":"Nürnberger Str."},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"97cb3ccd-dc53-443b-8287-053d4cffa89b","arrivalTime":{"target":"2021-12-22T18:32:00.000Z","predicted":"2021-12-22T18:32:38.000Z","predictedTimeInMs":1640197958000,"targetTimeInMs":1640197920000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:33:00.000Z","predicted":"2021-12-22T18:33:18.000Z","predictedTimeInMs":1640197998000,"targetTimeInMs":1640197980000,"timeType":"REAL","diff":0}},{"station":{"evaNo":"8005769","name":"S-Bad Cannstatt"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"02c6d5e7-5291-44ff-bf19-afa4ac62a112","arrivalTime":{"target":"2021-12-22T18:35:00.000Z","predicted":"2021-12-22T18:35:58.000Z","predictedTimeInMs":1640198158000,"targetTimeInMs":1640198100000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:36:00.000Z","predicted":"2021-12-22T18:36:35.000Z","predictedTimeInMs":1640198195000,"targetTimeInMs":1640198160000,"timeType":"REAL","diff":1}},{"station":{"evaNo":"8098096","name":"Stuttgart Hbf (tief)"},"status":"Normal","track":{"target":"101","prediction":"101"},"arrivalId":"f05bcca6-6f4f-4ab5-ba35-c224c4f9056b","arrivalTime":{"target":"2021-12-22T18:40:00.000Z","predicted":"2021-12-22T18:41:09.000Z","predictedTimeInMs":1640198469000,"targetTimeInMs":1640198400000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:40:00.000Z","predicted":"2021-12-22T18:41:58.000Z","predictedTimeInMs":1640198518000,"targetTimeInMs":1640198400000,"timeType":"REAL","diff":2}},{"station":{"evaNo":"8006700","name":"Stuttgart Stadtmitte"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"b3b63f3d-85aa-4768-ac46-57e218b8350d","arrivalTime":{"target":"2021-12-22T18:42:00.000Z","predicted":"2021-12-22T18:43:04.000Z","predictedTimeInMs":1640198584000,"targetTimeInMs":1640198520000,"timeType":"REAL","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:42:00.000Z","predicted":"2021-12-22T18:43:42.000Z","predictedTimeInMs":1640198622000,"targetTimeInMs":1640198520000,"timeType":"REAL","diff":2}},{"station":{"evaNo":"8006699","name":"Stuttgart Feuersee"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"eec2c82c-5586-4d89-9175-a6cd3680df24","arrivalTime":{"target":"2021-12-22T18:43:00.000Z","predicted":"2021-12-22T18:44:43.000Z","predictedTimeInMs":1640198683000,"targetTimeInMs":1640198580000,"timeType":"REAL","diff":2},"messages":[],"departureTime":{"target":"2021-12-22T18:44:00.000Z","predicted":"2021-12-22T18:45:00.000Z","predictedTimeInMs":1640198700000,"targetTimeInMs":1640198640000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8006698","name":"Stuttgart Schwabstr."},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"fb316746-de7e-4070-9c14-d59efae08b52","arrivalTime":{"target":"2021-12-22T18:45:00.000Z","predicted":"2021-12-22T18:46:00.000Z","predictedTimeInMs":1640198760000,"targetTimeInMs":1640198700000,"timeType":"PREVIEW","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:45:00.000Z","predicted":"2021-12-22T18:46:00.000Z","predictedTimeInMs":1640198760000,"targetTimeInMs":1640198700000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8006513","name":"Stuttgart Universität"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"5183aec4-cb6f-470d-9387-cf19c57f2dad","arrivalTime":{"target":"2021-12-22T18:50:00.000Z","predicted":"2021-12-22T18:50:00.000Z","predictedTimeInMs":1640199000000,"targetTimeInMs":1640199000000,"timeType":"PREVIEW","diff":0},"messages":[],"departureTime":{"target":"2021-12-22T18:50:00.000Z","predicted":"2021-12-22T18:51:00.000Z","predictedTimeInMs":1640199060000,"targetTimeInMs":1640199000000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8005779","name":"Stuttgart-Österfeld"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"aba98ed5-744d-4193-b9d3-0167effdb1bf","arrivalTime":{"target":"2021-12-22T18:52:00.000Z","predicted":"2021-12-22T18:52:00.000Z","predictedTimeInMs":1640199120000,"targetTimeInMs":1640199120000,"timeType":"PREVIEW","diff":0},"messages":[],"departureTime":{"target":"2021-12-22T18:52:00.000Z","predicted":"2021-12-22T18:53:00.000Z","predictedTimeInMs":1640199180000,"targetTimeInMs":1640199120000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8005776","name":"Stuttgart-Vaihingen"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"0f48dd54-ed3c-4363-b4bd-179a6796c3c8","arrivalTime":{"target":"2021-12-22T18:54:00.000Z","predicted":"2021-12-22T18:55:00.000Z","predictedTimeInMs":1640199300000,"targetTimeInMs":1640199240000,"timeType":"PREVIEW","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:55:00.000Z","predicted":"2021-12-22T18:56:00.000Z","predictedTimeInMs":1640199360000,"targetTimeInMs":1640199300000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8005773","name":"Stuttgart-Rohr"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"28293b3e-56fa-4e59-8c22-04a82fc70b67","arrivalTime":{"target":"2021-12-22T18:56:00.000Z","predicted":"2021-12-22T18:57:00.000Z","predictedTimeInMs":1640199420000,"targetTimeInMs":1640199360000,"timeType":"PREVIEW","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:57:00.000Z","predicted":"2021-12-22T18:58:00.000Z","predictedTimeInMs":1640199480000,"targetTimeInMs":1640199420000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8004496","name":"Oberaichen"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"1b1a9a32-70b2-4ad9-a0a4-fa876f71826f","arrivalTime":{"target":"2021-12-22T18:59:00.000Z","predicted":"2021-12-22T19:00:00.000Z","predictedTimeInMs":1640199600000,"targetTimeInMs":1640199540000,"timeType":"PREVIEW","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T18:59:00.000Z","predicted":"2021-12-22T19:00:00.000Z","predictedTimeInMs":1640199600000,"targetTimeInMs":1640199540000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8003622","name":"Leinfelden"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"cd59f9e5-f3c7-471b-80e7-1cefa110e84c","arrivalTime":{"target":"2021-12-22T19:01:00.000Z","predicted":"2021-12-22T19:02:00.000Z","predictedTimeInMs":1640199720000,"targetTimeInMs":1640199660000,"timeType":"PREVIEW","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T19:02:00.000Z","predicted":"2021-12-22T19:03:00.000Z","predictedTimeInMs":1640199780000,"targetTimeInMs":1640199720000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8001650","name":"Echterdingen"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"0f06b36b-3e6d-42a6-b1db-545298a9fc01","arrivalTime":{"target":"2021-12-22T19:04:00.000Z","predicted":"2021-12-22T19:05:00.000Z","predictedTimeInMs":1640199900000,"targetTimeInMs":1640199840000,"timeType":"PREVIEW","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T19:05:00.000Z","predicted":"2021-12-22T19:06:00.000Z","predictedTimeInMs":1640199960000,"targetTimeInMs":1640199900000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8005768","name":"Flughafen/Messe"},"status":"Normal","track":{"target":"1","prediction":"1"},"arrivalId":"d2cd9715-aa7e-4296-a55c-e7018c0d662e","arrivalTime":{"target":"2021-12-22T19:07:00.000Z","predicted":"2021-12-22T19:08:00.000Z","predictedTimeInMs":1640200080000,"targetTimeInMs":1640200020000,"timeType":"PREVIEW","diff":1},"messages":[],"departureTime":{"target":"2021-12-22T19:08:00.000Z","predicted":"2021-12-22T19:09:00.000Z","predictedTimeInMs":1640200140000,"targetTimeInMs":1640200080000,"timeType":"PREVIEW","diff":1}},{"station":{"evaNo":"8001984","name":"Filderstadt"},"status":"Normal","track":{"target":"2","prediction":"2"},"arrivalId":"0364faf0-1026-48f9-8f6d-e9e1d05d2a1e","arrivalTime":{"target":"2021-12-22T19:12:00.000Z","predicted":"2021-12-22T19:12:00.000Z","predictedTimeInMs":1640200320000,"targetTimeInMs":1640200320000,"timeType":"PREVIEW","diff":0},"messages":[]}],"hims":[{"id":"e80b183a19b720b7cdbf3cb8e2e37836d1a0086965b302bc0682981e97dca84c","caption":"In Stadtmitte wird der Aufzug zum S-Bahnsteig ausgetauscht. Der vorhandene Schrägaufzug wird komplett abgebaut und ein neuer Aufzug eingebuat. Der neue Aufzug soll nach derzeitiger Planung Ende Februar 2022 in Betrieb gehen. Die Deutsche Bahn empfiehlt mobilitätseingeschränkten Reisenden während den Bauarbeiten ihre Reise vorher (spätestens einen Werktag) bei der DB Mobilitätsservice-Zentrale (Telefon 030 / 6521 2888) anzumelden oder auf die S-Bahnhöfe Stuttgart Hbf (tief) oder Schwabstraße auszuweichen.
","shortText":"Die Deutsche Bahn erneuert am S-Bahnhof Stuttgart-Stadtmitte bis vsl. Ende Februar 2022 den Aufzug am Abgang Rotebühlplatz zur S-Bahn Station."},{"id":"83bc96354f39b0362a727387cf41d6131b9770b6fdf3dc4472bb5ee433f9b834","caption":"Momentan finden Aufzugsarbeiten am S-Bahnhof Feuersee statt. Während der Arbeiten stehen nicht alle Aufzüge bis voraussichtlich Mitte Februar 2022 zur Verfügung. Die Deutsche Bahn empfiehlt mobilitätseingeschränkten Reisenden während den Bauarbeiten ihre Reise vorher (spätestens einen Werktag) bei der DB Mobilitätsservice-Zentrale (Telefon 030 / 6521 2888) anzumelden oder auf die S-Bahnhöfe Schwabstraße oder Stuttgart Hbf (tief) auszuweichen.","shortText":"Die Deutsche Bahn erneuert die Aufzüge im S-Bahnhof Stuttgart-Feuersee bis vsl. Mitte Februar 2022"},{"id":"830711cadea4b14c8672022ef5c3f705ce2e681ff5b79ae43a12102815421b98","caption":"Am S-Bahnhof Winterbach werden beide Aufzüge erneuert. Während den Arbeiten stehen beide Aufzüge nicht zur Verfügung, der Fußgängersteg bleibt geöffnet. Ein stufenfreier Zugang zu beiden Bahnsteigen ist über die Straßenunterführung Oberdorf weiterhin möglich.","shortText":"Aufzugsarbeiten S-Bahnhof Winterbach, eingeschränkte Nutzungsmöglichkeit"}],"validFrom":"2021-12-22T17:53:23.000Z","validUntil":"2021-12-22T19:22:00.000Z","uic":"948004300661","trainNo":"7855 2021-12-22"} 2 | -------------------------------------------------------------------------------- /Sources/Train-API/Debug Data/status.json: -------------------------------------------------------------------------------- 1 | {"connection":true,"serviceLevel":"AVAILABLE_SERVICE","gpsStatus":"VALID","internet":"HIGH","latitude":52.534493,"longitude":13.197972833333333,"tileY":281,"tileX":256,"series":"807","serverTime":1637085624340,"speed":265.0,"trainType":"ICE","tzn":"Tz9001","wagonClass":"SECOND","connectivity":{"currentState":"HIGH","nextState":"UNSTABLE","remainingTimeSeconds":600},"bapInstalled":true} 2 | -------------------------------------------------------------------------------- /Sources/Train-API/Debug Data/tripInfo1.json: -------------------------------------------------------------------------------- 1 | {"trip":{"tripDate":"2021-11-16","trainType":"ICE","vzn":"542","actualPosition":251312,"distanceFromLastStop":8181,"totalDistance":507326,"stopInfo":{"scheduledNext":"8000036_00","actualNext":"8000036_00","actualLast":"8000152_00","actualLastStarted":"8000036","finalStationName":"Düsseldorf Hbf","finalStationEvaNr":"8000085_00"},"stops":[{"station":{"evaNr":"8010255_00","name":"Berlin Ostbahnhof","code":null,"geocoordinates":{"latitude":52.510488,"longitude":13.434681}},"timetable":{"scheduledArrivalTime":null,"actualArrivalTime":null,"showActualArrivalTime":null,"arrivalDelay":"","scheduledDepartureTime":1637084100000,"actualDepartureTime":1637084100000,"showActualDepartureTime":true,"departureDelay":""},"track":{"scheduled":"6","actual":"6"},"info":{"status":0,"passed":true,"positionStatus":"passed","distance":0,"distanceFromStart":0},"delayReasons":null},{"station":{"evaNr":"8011160_00","name":"Berlin Hbf","code":null,"geocoordinates":{"latitude":52.525592,"longitude":13.369545}},"timetable":{"scheduledArrivalTime":1637084520000,"actualArrivalTime":1637084580000,"showActualArrivalTime":true,"arrivalDelay":"+1","scheduledDepartureTime":1637084760000,"actualDepartureTime":1637084820000,"showActualDepartureTime":true,"departureDelay":"+1"},"track":{"scheduled":"13","actual":"13"},"info":{"status":0,"passed":true,"positionStatus":"passed","distance":4718,"distanceFromStart":4718},"delayReasons":null},{"station":{"evaNr":"8010404_00","name":"Berlin-Spandau","code":null,"geocoordinates":{"latitude":52.534648,"longitude":13.196898}},"timetable":{"scheduledArrivalTime":1637085540000,"actualArrivalTime":1637085600000,"showActualArrivalTime":true,"arrivalDelay":"+1","scheduledDepartureTime":1637085720000,"actualDepartureTime":1637085720000,"showActualDepartureTime":true,"departureDelay":""},"track":{"scheduled":"4","actual":"444"},"info":{"status":0,"passed":true,"positionStatus":"passed","distance":11725,"distanceFromStart":16443},"delayReasons":null},{"station":{"evaNr":"8000152_00","name":"Hannover Hbf","code":null,"geocoordinates":{"latitude":52.376761,"longitude":9.741021}},"timetable":{"scheduledArrivalTime":1637090880000,"actualArrivalTime":1637091120000,"showActualArrivalTime":true,"arrivalDelay":"+4","scheduledDepartureTime":1637091060000,"actualDepartureTime":1637091360000,"showActualDepartureTime":true,"departureDelay":"+5"},"track":{"scheduled":"12","actual":"12"},"info":{"status":0,"passed":true,"positionStatus":"departed","distance":234869,"distanceFromStart":251312},"delayReasons":null},{"station":{"evaNr":"8000036_00","name":"Bielefeld Hbf","code":null,"geocoordinates":{"latitude":52.029261,"longitude":8.532722}},"timetable":{"scheduledArrivalTime":1637094060000,"actualArrivalTime":1637094240000,"showActualArrivalTime":true,"arrivalDelay":"+3","scheduledDepartureTime":1637094180000,"actualDepartureTime":1637094360000,"showActualDepartureTime":true,"departureDelay":"+3"},"track":{"scheduled":"4","actual":"4"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":90982,"distanceFromStart":342294},"delayReasons":null},{"station":{"evaNr":"8000149_00","name":"Hamm (Westf) Hbf","code":null,"geocoordinates":{"latitude":51.678078,"longitude":7.807821}},"timetable":{"scheduledArrivalTime":1637095680000,"actualArrivalTime":1637095860000,"showActualArrivalTime":true,"arrivalDelay":"+3","scheduledDepartureTime":1637095860000,"actualDepartureTime":1637096040000,"showActualDepartureTime":true,"departureDelay":"+3"},"track":{"scheduled":"10","actual":"10"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":63292,"distanceFromStart":405586},"delayReasons":null},{"station":{"evaNr":"8000080_00","name":"Dortmund Hbf","code":null,"geocoordinates":{"latitude":51.517896,"longitude":7.45929}},"timetable":{"scheduledArrivalTime":1637097000000,"actualArrivalTime":1637097120000,"showActualArrivalTime":true,"arrivalDelay":"+2","scheduledDepartureTime":1637097120000,"actualDepartureTime":1637097240000,"showActualDepartureTime":true,"departureDelay":"+2"},"track":{"scheduled":"20","actual":"20"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":29955,"distanceFromStart":435541},"delayReasons":null},{"station":{"evaNr":"8000041_00","name":"Bochum Hbf","code":null,"geocoordinates":{"latitude":51.478609,"longitude":7.223275}},"timetable":{"scheduledArrivalTime":1637097780000,"actualArrivalTime":1637097960000,"showActualArrivalTime":true,"arrivalDelay":"+3","scheduledDepartureTime":1637097900000,"actualDepartureTime":1637098020000,"showActualDepartureTime":true,"departureDelay":"+2"},"track":{"scheduled":"3","actual":"3"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":16916,"distanceFromStart":452457},"delayReasons":null},{"station":{"evaNr":"8000098_00","name":"Essen Hbf","code":null,"geocoordinates":{"latitude":51.451355,"longitude":7.014793}},"timetable":{"scheduledArrivalTime":1637098500000,"actualArrivalTime":1637098500000,"showActualArrivalTime":true,"arrivalDelay":"","scheduledDepartureTime":1637098620000,"actualDepartureTime":1637098620000,"showActualDepartureTime":true,"departureDelay":""},"track":{"scheduled":"1","actual":"1"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":14761,"distanceFromStart":467218},"delayReasons":null},{"station":{"evaNr":"8000086_00","name":"Duisburg Hbf","code":null,"geocoordinates":{"latitude":51.429785,"longitude":6.775903}},"timetable":{"scheduledArrivalTime":1637099280000,"actualArrivalTime":1637099400000,"showActualArrivalTime":true,"arrivalDelay":"+2","scheduledDepartureTime":1637099460000,"actualDepartureTime":1637099580000,"showActualDepartureTime":true,"departureDelay":"+2"},"track":{"scheduled":"4","actual":"4"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":16735,"distanceFromStart":483953},"delayReasons":null},{"station":{"evaNr":"8000085_00","name":"Düsseldorf Hbf","code":null,"geocoordinates":{"latitude":51.219962,"longitude":6.794319}},"timetable":{"scheduledArrivalTime":1637100900000,"actualArrivalTime":1637100900000,"showActualArrivalTime":true,"arrivalDelay":"","scheduledDepartureTime":null,"actualDepartureTime":null,"showActualDepartureTime":null,"departureDelay":""},"track":{"scheduled":"5","actual":"5"},"info":{"status":0,"passed":false,"positionStatus":"future","distance":23373,"distanceFromStart":507326},"delayReasons":null}]},"connection":null,"selectedRoute":{"conflictInfo":{"status":"NO_CONFLICT","text":null},"mobility":null},"active":null} 2 | -------------------------------------------------------------------------------- /Sources/Train-API/Debug Data/tripInfo2.json: -------------------------------------------------------------------------------- 1 | { 2 | "trip":{ 3 | "tripDate":"2021-11-16", 4 | "trainType":"ICE", 5 | "vzn":"542", 6 | "actualPosition":251312, 7 | "distanceFromLastStop":8181, 8 | "totalDistance":507326, 9 | "stopInfo":{ 10 | "scheduledNext":"8000036_00", 11 | "actualNext":"8000036_00", 12 | "actualLast":"8000152_00", 13 | "actualLastStarted":"8000036", 14 | "finalStationName":"Düsseldorf Hbf", 15 | "finalStationEvaNr":"8000085_00" 16 | }, 17 | "stops":[ 18 | { 19 | "station":{ 20 | "evaNr":"8010255_00", 21 | "name":"Berlin Hundbrunnen", 22 | "code":null, 23 | "geocoordinates":{ 24 | "latitude":52.510488, 25 | "longitude":13.434681 26 | } 27 | }, 28 | "timetable":{ 29 | "scheduledArrivalTime":null, 30 | "actualArrivalTime":null, 31 | "showActualArrivalTime":null, 32 | "arrivalDelay":"", 33 | "scheduledDepartureTime":1637084100000, 34 | "actualDepartureTime":1637084100000, 35 | "showActualDepartureTime":true, 36 | "departureDelay":"" 37 | }, 38 | "track":{ 39 | "scheduled":"6", 40 | "actual":"6" 41 | }, 42 | "info":{ 43 | "status":0, 44 | "passed":true, 45 | "positionStatus":"passed", 46 | "distance":0, 47 | "distanceFromStart":0 48 | }, 49 | "delayReasons":null 50 | }, 51 | { 52 | "station":{ 53 | "evaNr":"8010255_00", 54 | "name":"Berlin Reichenberger Str.", 55 | "code":null, 56 | "geocoordinates":{ 57 | "latitude":52.510488, 58 | "longitude":13.434681 59 | } 60 | }, 61 | "timetable":{ 62 | "scheduledArrivalTime":null, 63 | "actualArrivalTime":null, 64 | "showActualArrivalTime":null, 65 | "arrivalDelay":"", 66 | "scheduledDepartureTime":1637084100000, 67 | "actualDepartureTime":1637084100000, 68 | "showActualDepartureTime":true, 69 | "departureDelay":"" 70 | }, 71 | "track":{ 72 | "scheduled":"6", 73 | "actual":"6" 74 | }, 75 | "info":{ 76 | "status":0, 77 | "passed":true, 78 | "positionStatus":"passed", 79 | "distance":0, 80 | "distanceFromStart":0 81 | }, 82 | "delayReasons":null 83 | }, 84 | { 85 | "station":{ 86 | "evaNr":"8011160_00", 87 | "name":"Berlin Hbf", 88 | "code":null, 89 | "geocoordinates":{ 90 | "latitude":52.525592, 91 | "longitude":13.369545 92 | } 93 | }, 94 | "timetable":{ 95 | "scheduledArrivalTime":1637084520000, 96 | "actualArrivalTime":1637084580000, 97 | "showActualArrivalTime":true, 98 | "arrivalDelay":"+1", 99 | "scheduledDepartureTime":1637084760000, 100 | "actualDepartureTime":1637084820000, 101 | "showActualDepartureTime":true, 102 | "departureDelay":"+1" 103 | }, 104 | "track":{ 105 | "scheduled":"13", 106 | "actual":"13" 107 | }, 108 | "info":{ 109 | "status":0, 110 | "passed":true, 111 | "positionStatus":"passed", 112 | "distance":4718, 113 | "distanceFromStart":4718 114 | }, 115 | "delayReasons":null 116 | }, 117 | { 118 | "station":{ 119 | "evaNr":"8010404_00", 120 | "name":"Berlin-Spandau", 121 | "code":null, 122 | "geocoordinates":{ 123 | "latitude":52.534648, 124 | "longitude":13.196898 125 | } 126 | }, 127 | "timetable":{ 128 | "scheduledArrivalTime":1637085540000, 129 | "actualArrivalTime":1637085600000, 130 | "showActualArrivalTime":true, 131 | "arrivalDelay":"+1", 132 | "scheduledDepartureTime":1637085720000, 133 | "actualDepartureTime":1637085720000, 134 | "showActualDepartureTime":true, 135 | "departureDelay":"" 136 | }, 137 | "track":{ 138 | "scheduled":"4", 139 | "actual":"4" 140 | }, 141 | "info":{ 142 | "status":0, 143 | "passed":true, 144 | "positionStatus":"passed", 145 | "distance":11725, 146 | "distanceFromStart":16443 147 | }, 148 | "delayReasons":null 149 | }, 150 | { 151 | "station":{ 152 | "evaNr":"8000152_00", 153 | "name":"Hannover Hbf", 154 | "code":null, 155 | "geocoordinates":{ 156 | "latitude":52.376761, 157 | "longitude":9.741021 158 | } 159 | }, 160 | "timetable":{ 161 | "scheduledArrivalTime":1637090880000, 162 | "actualArrivalTime":1637091120000, 163 | "showActualArrivalTime":true, 164 | "arrivalDelay":"+4", 165 | "scheduledDepartureTime":1637091060000, 166 | "actualDepartureTime":1637091360000, 167 | "showActualDepartureTime":true, 168 | "departureDelay":"+5" 169 | }, 170 | "track":{ 171 | "scheduled":"12", 172 | "actual":"12" 173 | }, 174 | "info":{ 175 | "status":0, 176 | "passed":true, 177 | "positionStatus":"departed", 178 | "distance":234869, 179 | "distanceFromStart":251312 180 | }, 181 | "delayReasons":null 182 | }, 183 | { 184 | "station":{ 185 | "evaNr":"8000036_00", 186 | "name":"Bielefeld Hbf", 187 | "code":null, 188 | "geocoordinates":{ 189 | "latitude":52.029261, 190 | "longitude":8.532722 191 | } 192 | }, 193 | "timetable":{ 194 | "scheduledArrivalTime":1637094060000, 195 | "actualArrivalTime":1637094240000, 196 | "showActualArrivalTime":true, 197 | "arrivalDelay":"+3", 198 | "scheduledDepartureTime":1637094180000, 199 | "actualDepartureTime":1637094360000, 200 | "showActualDepartureTime":true, 201 | "departureDelay":"+3" 202 | }, 203 | "track":{ 204 | "scheduled":"4", 205 | "actual":"4" 206 | }, 207 | "info":{ 208 | "status":0, 209 | "passed":false, 210 | "positionStatus":"future", 211 | "distance":90982, 212 | "distanceFromStart":342294 213 | }, 214 | "delayReasons":null 215 | }, 216 | { 217 | "station":{ 218 | "evaNr":"8000149_00", 219 | "name":"Hamm (Westf) Hbf", 220 | "code":null, 221 | "geocoordinates":{ 222 | "latitude":51.678078, 223 | "longitude":7.807821 224 | } 225 | }, 226 | "timetable":{ 227 | "scheduledArrivalTime":1637095680000, 228 | "actualArrivalTime":1637095860000, 229 | "showActualArrivalTime":true, 230 | "arrivalDelay":"+3", 231 | "scheduledDepartureTime":1637095860000, 232 | "actualDepartureTime":1637096040000, 233 | "showActualDepartureTime":true, 234 | "departureDelay":"+3" 235 | }, 236 | "track":{ 237 | "scheduled":"10", 238 | "actual":"10" 239 | }, 240 | "info":{ 241 | "status":0, 242 | "passed":false, 243 | "positionStatus":"future", 244 | "distance":63292, 245 | "distanceFromStart":405586 246 | }, 247 | "delayReasons":null 248 | }, 249 | { 250 | "station":{ 251 | "evaNr":"8000080_00", 252 | "name":"Dortmund Hbf", 253 | "code":null, 254 | "geocoordinates":{ 255 | "latitude":51.517896, 256 | "longitude":7.45929 257 | } 258 | }, 259 | "timetable":{ 260 | "scheduledArrivalTime":1637097000000, 261 | "actualArrivalTime":1637097120000, 262 | "showActualArrivalTime":true, 263 | "arrivalDelay":"+2", 264 | "scheduledDepartureTime":1637097120000, 265 | "actualDepartureTime":1637097240000, 266 | "showActualDepartureTime":true, 267 | "departureDelay":"+2" 268 | }, 269 | "track":{ 270 | "scheduled":"20", 271 | "actual":"20" 272 | }, 273 | "info":{ 274 | "status":0, 275 | "passed":false, 276 | "positionStatus":"future", 277 | "distance":29955, 278 | "distanceFromStart":435541 279 | }, 280 | "delayReasons":null 281 | }, 282 | { 283 | "station":{ 284 | "evaNr":"8000041_00", 285 | "name":"Bochum Hbf", 286 | "code":null, 287 | "geocoordinates":{ 288 | "latitude":51.478609, 289 | "longitude":7.223275 290 | } 291 | }, 292 | "timetable":{ 293 | "scheduledArrivalTime":1637097780000, 294 | "actualArrivalTime":1637097960000, 295 | "showActualArrivalTime":true, 296 | "arrivalDelay":"+3", 297 | "scheduledDepartureTime":1637097900000, 298 | "actualDepartureTime":1637098020000, 299 | "showActualDepartureTime":true, 300 | "departureDelay":"+2" 301 | }, 302 | "track":{ 303 | "scheduled":"3", 304 | "actual":"3" 305 | }, 306 | "info":{ 307 | "status":0, 308 | "passed":false, 309 | "positionStatus":"future", 310 | "distance":16916, 311 | "distanceFromStart":452457 312 | }, 313 | "delayReasons":null 314 | }, 315 | { 316 | "station":{ 317 | "evaNr":"8000098_00", 318 | "name":"Essen Hbf", 319 | "code":null, 320 | "geocoordinates":{ 321 | "latitude":51.451355, 322 | "longitude":7.014793 323 | } 324 | }, 325 | "timetable":{ 326 | "scheduledArrivalTime":1637098500000, 327 | "actualArrivalTime":1637098500000, 328 | "showActualArrivalTime":true, 329 | "arrivalDelay":"", 330 | "scheduledDepartureTime":1637098620000, 331 | "actualDepartureTime":1637098620000, 332 | "showActualDepartureTime":true, 333 | "departureDelay":"+22" 334 | }, 335 | "track":{ 336 | "scheduled":"1", 337 | "actual":"1" 338 | }, 339 | "info":{ 340 | "status":0, 341 | "passed":false, 342 | "positionStatus":"future", 343 | "distance":14761, 344 | "distanceFromStart":467218 345 | }, 346 | "delayReasons":null 347 | }, 348 | { 349 | "station":{ 350 | "evaNr":"8000086_00", 351 | "name":"Duisburg Hbf", 352 | "code":null, 353 | "geocoordinates":{ 354 | "latitude":51.429785, 355 | "longitude":6.775903 356 | } 357 | }, 358 | "timetable":{ 359 | "scheduledArrivalTime":1637099280000, 360 | "actualArrivalTime":1637099400000, 361 | "showActualArrivalTime":true, 362 | "arrivalDelay":"+2", 363 | "scheduledDepartureTime":1637099460000, 364 | "actualDepartureTime":1637099580000, 365 | "showActualDepartureTime":true, 366 | "departureDelay":"+2" 367 | }, 368 | "track":{ 369 | "scheduled":"4", 370 | "actual":"4" 371 | }, 372 | "info":{ 373 | "status":0, 374 | "passed":false, 375 | "positionStatus":"future", 376 | "distance":16735, 377 | "distanceFromStart":483953 378 | }, 379 | "delayReasons":null 380 | }, 381 | { 382 | "station":{ 383 | "evaNr":"8000085_00", 384 | "name":"Düsseldorf Hbf", 385 | "code":null, 386 | "geocoordinates":{ 387 | "latitude":51.219962, 388 | "longitude":6.794319 389 | } 390 | }, 391 | "timetable":{ 392 | "scheduledArrivalTime":1637100900000, 393 | "actualArrivalTime":1637100900000, 394 | "showActualArrivalTime":true, 395 | "arrivalDelay":"", 396 | "scheduledDepartureTime":null, 397 | "actualDepartureTime":null, 398 | "showActualDepartureTime":null, 399 | "departureDelay":"" 400 | }, 401 | "track":{ 402 | "scheduled":"5", 403 | "actual":"5" 404 | }, 405 | "info":{ 406 | "status":0, 407 | "passed":false, 408 | "positionStatus":"future", 409 | "distance":23373, 410 | "distanceFromStart":507326 411 | }, 412 | "delayReasons":null 413 | } 414 | ] 415 | }, 416 | "connection":null, 417 | "selectedRoute":{ 418 | "conflictInfo":{ 419 | "status":"NO_CONFLICT", 420 | "text":null 421 | }, 422 | "mobility":null 423 | }, 424 | "active":null 425 | } 426 | -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR401.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR401.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR402.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR402.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR403 Serie 1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR403 Serie 1.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR403 Serie 2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR403 Serie 2.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR406.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR406.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR407.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR407.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR411 Serie 1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR411 Serie 1.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR411 Serie 2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR411 Serie 2.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR412.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR412.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICE Baureihen/BR415.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/ICE Baureihen/BR415.pdf -------------------------------------------------------------------------------- /Sources/Train-API/ICEConnection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ICEConnection.swift 3 | // ICE Buddy 4 | // 5 | // Created by Frederik Riedel on 16.11.21. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | import CoreLocation 11 | import AppKit 12 | import FredKit 13 | 14 | public class ICEConnection { 15 | 16 | public static let shared = ICEConnection() 17 | 18 | public func loadCurrentTrainData(completion: @escaping (TrainMetaData?) -> Void) { 19 | AF.request("https://iceportal.de/api1/rs/status").responseJSON { response in 20 | if let result = response.value as? [String: Any] { 21 | let metaData = TrainMetaData(dict: result) 22 | completion(metaData) 23 | } else { 24 | #if DEBUG 25 | print(Bundle.module) 26 | if let data = try? Data(contentsOf: Bundle.module.url(forResource: "ice", withExtension: "json")!) { 27 | let jsonResult = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves) 28 | if let jsonResult = jsonResult as? [String: Any] { 29 | let metaData = TrainMetaData(dict: jsonResult) 30 | completion(metaData) 31 | } 32 | } 33 | #else 34 | // currently not connected to ICE? 35 | completion(nil) 36 | #endif 37 | } 38 | } 39 | } 40 | 41 | public func loadCurrentTripData(completion: @escaping (TripData?) -> Void) { 42 | AF.request("https://iceportal.de/api1/rs/tripInfo/trip").responseJSON { response in 43 | if let result = response.value as? [String: Any] { 44 | let metaData = TripData(dict: result) 45 | 46 | 47 | 48 | completion(metaData) 49 | } else { 50 | #if DEBUG 51 | 52 | 53 | if let data = try? Data(contentsOf: Bundle.module.url(forResource: "paris", withExtension: "json")!) { 54 | let jsonResult = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves) 55 | if let jsonResult = jsonResult as? [String: Any] { 56 | let metaData = TripData(dict: jsonResult) 57 | 58 | completion(metaData) 59 | } 60 | } 61 | #else 62 | // currently not connected to ICE? 63 | completion(nil) 64 | #endif 65 | } 66 | } 67 | } 68 | 69 | } 70 | 71 | extension Array where Element == Int { 72 | private func number(number: Int, matchesWithTzn tzn: String) -> Bool { 73 | tzn.lowercased() == "tz\(number)" || tzn.lowercased() == "tz \(number)" || tzn.lowercased() == "ice\(number)" || tzn.lowercased() == "ice \(number)" || 74 | tzn.lowercased() == "tz0\(number)" || tzn.lowercased() == "tz 0\(number)" || tzn.lowercased() == "ice0\(number)" || tzn.lowercased() == "ice 0\(number)" 75 | } 76 | 77 | func contains(triebzugnummer: String) -> Bool { 78 | self.contains(where: { number in 79 | self.number(number: number, matchesWithTzn: triebzugnummer) 80 | }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Sources/Train-API/InternetConnection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Frederik Riedel on 05.06.22. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum InternetConnection { 11 | case high, unstable 12 | 13 | static func from(rawString: String) -> InternetConnection { 14 | if rawString == "HIGH" { 15 | return .high 16 | } 17 | return .unstable 18 | } 19 | 20 | public var localizedString: String { 21 | switch self { 22 | case .high: 23 | return "High" 24 | case .unstable: 25 | return "Unstable" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Train-API/JourneyStop.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Frederik Riedel on 05.06.22. 6 | // 7 | 8 | import Foundation 9 | import CoreLocation 10 | 11 | public struct JourneyStop { 12 | public let evaNr: String 13 | public let name: String 14 | 15 | public let actualDepartureTime: Date? 16 | public let actualArrivalTime: Date? 17 | 18 | public let scheduledDepartureTime: Date? 19 | let scheduledArrivalTime: Date? 20 | 21 | public let departureDelay: String 22 | public let scheduledTrack: String 23 | public let actualTrack: String 24 | 25 | public let passed: Bool 26 | 27 | public let coordinate: CLLocationCoordinate2D 28 | 29 | public var humanReadableArrivalTime: String { 30 | if let actualArrivalTime = actualArrivalTime { 31 | return actualArrivalTime.minuteTimeString 32 | } 33 | 34 | if let scheduledArrivalTime = scheduledArrivalTime { 35 | return scheduledArrivalTime.minuteTimeString 36 | } 37 | 38 | if let actualDepartureTime = actualDepartureTime { 39 | return actualDepartureTime.minuteTimeString 40 | } 41 | 42 | if let scheduledDepartureTime = scheduledDepartureTime { 43 | return scheduledDepartureTime.minuteTimeString 44 | } 45 | 46 | 47 | return "Time Unknown" 48 | } 49 | 50 | public init?(dict: [String: Any]) { 51 | if let station = dict["station"] as? [String: Any] { 52 | 53 | if let evaNr = station["evaNr"] as? String { 54 | self.evaNr = evaNr 55 | } else { 56 | return nil 57 | } 58 | 59 | if let stopName = station["name"] as? String { 60 | self.name = stopName 61 | } else { 62 | return nil 63 | } 64 | 65 | if let geocoordinates = station["geocoordinates"] as? [String: Double] { 66 | if let latitude = geocoordinates["latitude"], let longitude = geocoordinates["longitude"] { 67 | self.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude) 68 | } else { 69 | return nil 70 | } 71 | } else { 72 | return nil 73 | } 74 | 75 | } else { 76 | return nil 77 | } 78 | 79 | if let info = dict["info"] as? [String: Any] { 80 | if let passed = info["passed"] as? Bool { 81 | self.passed = passed 82 | } else { 83 | return nil 84 | } 85 | } else { 86 | return nil 87 | } 88 | 89 | if let track = dict["track"] as? [String: Any] { 90 | 91 | if let actualTrack = track["actual"] as? String { 92 | if actualTrack.isEmpty { 93 | self.actualTrack = "–" 94 | } else { 95 | self.actualTrack = actualTrack 96 | } 97 | 98 | } else { 99 | return nil 100 | } 101 | 102 | if let scheduledTrack = track["scheduled"] as? String { 103 | self.scheduledTrack = scheduledTrack 104 | } else { 105 | self.scheduledTrack = "–" 106 | } 107 | 108 | } else { 109 | return nil 110 | } 111 | 112 | if let timetable = dict["timetable"] as? [String: Any] { 113 | if let actualDepartureTime = timetable["actualDepartureTime"] as? Double { 114 | self.actualDepartureTime = Date(timeIntervalSince1970: actualDepartureTime / 1000.0) 115 | } else { 116 | self.actualDepartureTime = nil 117 | } 118 | 119 | if let actualArrivalTime = timetable["actualArrivalTime"] as? Double { 120 | self.actualArrivalTime = Date(timeIntervalSince1970: actualArrivalTime / 1000.0) 121 | } else { 122 | self.actualArrivalTime = nil 123 | } 124 | 125 | if let scheduledArrivalTime = timetable["scheduledArrivalTime"] as? Double { 126 | self.scheduledArrivalTime = Date(timeIntervalSince1970: scheduledArrivalTime / 1000.0) 127 | } else { 128 | self.scheduledArrivalTime = nil 129 | } 130 | 131 | if let scheduledDepartureTime = timetable["scheduledDepartureTime"] as? Double { 132 | self.scheduledDepartureTime = Date(timeIntervalSince1970: scheduledDepartureTime / 1000.0) 133 | } else { 134 | self.scheduledDepartureTime = nil 135 | } 136 | 137 | if let departureDelay = timetable["departureDelay"] as? String { 138 | self.departureDelay = departureDelay 139 | } else { 140 | return nil 141 | } 142 | 143 | } else { 144 | return nil 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR401.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ice 1.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR401.imageset/ice 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/Media.xcassets/BR401.imageset/ice 1.png -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR402.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ice 2.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR402.imageset/ice 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/Media.xcassets/BR402.imageset/ice 2.png -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR403.imageset/BR403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/Media.xcassets/BR403.imageset/BR403.png -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR403.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BR403.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR406.imageset/BR406.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/Media.xcassets/BR406.imageset/BR406.png -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR406.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BR406.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR407.imageset/BR407.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/Media.xcassets/BR407.imageset/BR407.png -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR407.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BR407.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR411.imageset/BR411.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/Media.xcassets/BR411.imageset/BR411.png -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR411.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BR411.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR412.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "ice 4.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR412.imageset/ice 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/Media.xcassets/BR412.imageset/ice 4.png -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR415.imageset/BR415.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ICE-Buddy/Train-API-Swift-Package/ea56a3e9b5a1738ecd2bbe7da0cfe02215550c43/Sources/Train-API/Media.xcassets/BR415.imageset/BR415.png -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/BR415.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BR415.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Train-API/Media.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/Train-API/TrainMetaData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Frederik Riedel on 05.06.22. 6 | // 7 | 8 | import Foundation 9 | import CoreLocation 10 | 11 | public struct TrainMetaData { 12 | public let speed: Double 13 | public let internetConnection: InternetConnection 14 | public let trainType: TrainType 15 | public let trainId: String 16 | public let currentLocation: CLLocationCoordinate2D 17 | public let timestamp: Date 18 | 19 | public init?(dict: [String: Any]) { 20 | 21 | if let speed = dict["speed"] as? Double { 22 | self.speed = speed 23 | } else { 24 | return nil 25 | } 26 | 27 | if let internetConnectivity = dict["connectivity"] as? [String: Any] { 28 | if let currentState = internetConnectivity["currentState"] as? String { 29 | self.internetConnection = InternetConnection.from(rawString: currentState) 30 | } else { 31 | return nil 32 | } 33 | } else { 34 | return nil 35 | } 36 | 37 | if let triebZugNummer = dict["tzn"] as? String { 38 | self.trainType = TrainType.trainType(from: triebZugNummer) 39 | self.trainId = triebZugNummer 40 | } else { 41 | return nil 42 | } 43 | 44 | if let longitude = dict["longitude"] as? Double, let latitude = dict["latitude"] as? Double { 45 | let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude) 46 | self.currentLocation = coordinate 47 | } else { 48 | return nil 49 | } 50 | 51 | if let serverTime = dict["serverTime"] as? Double { 52 | self.timestamp = Date(timeIntervalSince1970: serverTime / 1000) 53 | } else { 54 | return nil 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Train-API/TrainType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Frederik Riedel on 05.06.22. 6 | // 7 | 8 | import Foundation 9 | import AppKit 10 | 11 | public enum TrainType: CaseIterable { 12 | 13 | case BR401, BR402, BR403, BR406, BR407, BR408, BR411, BR415, BR412, unknown 14 | 15 | private var triebZugNummern: [Int] { 16 | switch self { 17 | case .BR401: 18 | return [Int](101...199) 19 | case .BR402: 20 | return [Int](201...299) 21 | case .BR403: 22 | return [Int](301...399) 23 | case .BR406: 24 | return [Int](4601...4699) 25 | case .BR407: 26 | return [Int](701...799) + [Int](4701...4799) 27 | case .BR408: 28 | return [Int](801...899) 29 | case .BR411: 30 | return [Int](1101...1199) 31 | case .BR415: 32 | return [Int](1501...1599) 33 | case .BR412: 34 | return [Int](9001...9999) 35 | case .unknown: 36 | return [] 37 | } 38 | } 39 | 40 | public var humanReadableTrainType: String { 41 | switch self { 42 | case .BR401: 43 | return "ICE 1" 44 | case .BR402: 45 | return "ICE 2" 46 | case .BR403, .BR406, .BR407: 47 | return "ICE 3" 48 | case .BR408: 49 | return "ICE 3 Neo" 50 | case .BR411, .BR415: 51 | return "ICE T" 52 | case .BR412: 53 | return "ICE 4" 54 | case .unknown: 55 | return "Unknown Train Type" 56 | } 57 | } 58 | 59 | 60 | public static func trainType(from triebZugNummer: String) -> TrainType { 61 | return TrainType.allCases.first { trainType in 62 | trainType.triebZugNummern.contains(triebzugnummer: triebZugNummer) 63 | } ?? .unknown 64 | } 65 | 66 | #if SWIFT_PACKAGE 67 | public var trainIcon: NSImage { 68 | 69 | 70 | switch self { 71 | case .BR401: 72 | return Bundle.module.image(forResource: "BR401")! 73 | case .BR402: 74 | return Bundle.module.image(forResource: "BR402")! 75 | case .BR403: 76 | return Bundle.module.image(forResource: "BR403")! 77 | case .BR406: 78 | return Bundle.module.image(forResource: "BR406")! 79 | case .BR407: 80 | return Bundle.module.image(forResource: "BR407")! 81 | case .BR408: 82 | return Bundle.module.image(forResource: "BR408")! 83 | case .BR411: 84 | return Bundle.module.image(forResource: "BR411")! 85 | case .BR415: 86 | return Bundle.module.image(forResource: "BR415")! 87 | case .BR412: 88 | return Bundle.module.image(forResource: "BR412")! 89 | case .unknown: 90 | return Bundle.module.image(forResource: "BR401")! 91 | } 92 | } 93 | #endif 94 | } 95 | -------------------------------------------------------------------------------- /Sources/Train-API/TripData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by Frederik Riedel on 05.06.22. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct TripData { 11 | public let trainId: String 12 | public let stops: [JourneyStop] 13 | 14 | public var startStop: JourneyStop? { 15 | return stops.first 16 | } 17 | 18 | public var finalStop: JourneyStop? { 19 | return stops.last 20 | } 21 | 22 | public var nextStop: JourneyStop? { 23 | stops.first { stop in 24 | !stop.passed 25 | } 26 | } 27 | 28 | 29 | 30 | init?(dict: [String: Any]) { 31 | if let tripDict = dict["trip"] as? [String: Any] { 32 | if let trainType = tripDict["trainType"] as? String, let connectionId = tripDict["vzn"] as? String { 33 | self.trainId = "\(trainType) \(connectionId)" 34 | } else { 35 | return nil 36 | } 37 | 38 | if let stops = tripDict["stops"] as? [ [String: Any] ] { 39 | self.stops = stops.compactMap({ stopDict in 40 | return JourneyStop(dict: stopDict) 41 | }) 42 | } else { 43 | return nil 44 | } 45 | 46 | 47 | 48 | } else { 49 | return nil 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tests/Train-APITests/Train_APITests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Train_API 3 | 4 | final class Train_APITests: XCTestCase { 5 | func testExample() throws { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | ICEConnection.shared.loadCurrentTripData { tripData in 10 | print(tripData?.finalStop) 11 | } 12 | } 13 | } 14 | --------------------------------------------------------------------------------