├── src ├── preload.js ├── icons │ ├── fia.png │ ├── edit.png │ ├── save.png │ ├── compass.png │ ├── denied.png │ ├── layout.png │ ├── noicon.png │ ├── peanut.png │ ├── restore.png │ ├── settings.png │ ├── arrowleft.png │ ├── arrowright.png │ ├── checkmark.png │ ├── teams │ │ ├── haas.png │ │ ├── alpine.png │ │ ├── ferrari.png │ │ ├── mclaren.png │ │ ├── renault.png │ │ ├── haas-red.png │ │ ├── mercedes.png │ │ ├── red-bull.png │ │ ├── williams.png │ │ ├── alfa-romeo.png │ │ ├── alpha-tauri.png │ │ ├── aston-martin.png │ │ ├── force-india.png │ │ ├── racing-point.png │ │ ├── toro-rosso.png │ │ ├── mclaren-white.png │ │ └── williams-white.png │ ├── tires │ │ ├── hard.png │ │ ├── soft.png │ │ ├── test.png │ │ ├── wet.png │ │ ├── medium.png │ │ ├── unknown.png │ │ ├── hard_real.png │ │ ├── soft_real.png │ │ ├── test_real.png │ │ ├── wet_real.png │ │ ├── intermediate.png │ │ ├── medium_real.png │ │ ├── test_unknown.png │ │ └── intermediate_real.png │ ├── green-heart.png │ ├── open-external.png │ ├── unknowndriver.png │ ├── windows │ │ ├── color.png │ │ ├── govee.png │ │ ├── logo.png │ │ ├── compass.png │ │ ├── weather.png │ │ ├── battlemode.png │ │ ├── sessionlog.png │ │ ├── singlercm.png │ │ ├── statuses.png │ │ ├── tirestats.png │ │ ├── trackinfo.png │ │ ├── tracktime.png │ │ ├── currentlaps.png │ │ ├── flagdisplay.png │ │ └── crashdetection.png │ ├── messages │ │ ├── blank.png │ │ ├── car_spun.png │ │ ├── grip_low.png │ │ ├── weather.png │ │ ├── car_stopped.png │ │ ├── correction.png │ │ ├── drs_enabled.png │ │ ├── grip_normal.png │ │ ├── car_missedapex.png │ │ ├── car_offtrack.png │ │ ├── drs_disabled.png │ │ ├── grip_slippery.png │ │ ├── incident_noted.png │ │ ├── penalty_time.png │ │ ├── pit_entry_open.png │ │ ├── pit_exit_open.png │ │ ├── session_resume.png │ │ ├── car_tracklimits.png │ │ ├── pit_entry_closed.png │ │ ├── pit_exit_closed.png │ │ ├── penalty_stopandgo.png │ │ ├── safetycar_pitlane.png │ │ ├── safetycar_startfinish.png │ │ ├── session_startdelayed.png │ │ ├── session_durationchanged.png │ │ ├── incident_nofurther_action.png │ │ ├── incident_underinvestigation.png │ │ ├── incident_nofurther_investigation.png │ │ └── incident_investigationafterthesession.png │ └── flags │ │ ├── flag_chequered.png │ │ ├── flag_blackandorange.png │ │ └── flag_blackandwhite.png ├── fonts │ ├── Inter-Bold.ttf │ ├── Formula1-Wide.ttf │ ├── Inter-Medium.ttf │ ├── Inter-Regular.ttf │ ├── TitilliumWeb-Bold.ttf │ ├── Formula1-Bold_web_0.ttf │ ├── TitilliumWeb-Italic.ttf │ ├── TitilliumWeb-Regular.ttf │ ├── Exo2-VariableFont_wght.ttf │ ├── Formula1-Regular_web_0.ttf │ ├── TitilliumWeb-BoldItalic.ttf │ ├── TitilliumWeb-SemiBold.ttf │ ├── Formula1-Bold_web_0_modified.ttf │ ├── TitilliumWeb-SemiBoldItalic.ttf │ └── fonts.css ├── statuses │ ├── slippery.png │ ├── index.html │ ├── style.css │ └── index.js ├── flagdisplay │ ├── Chequered.png │ ├── index.html │ ├── govee │ │ ├── index.html │ │ └── style.css │ ├── style.css │ └── index.js ├── weather │ ├── build │ │ └── js │ │ │ ├── 20b52d0c6ea89997befb.ttf │ │ │ ├── 425b19b61e022e504f51.ttf │ │ │ ├── 50c688fdc8d382c66303.ttf │ │ │ ├── 6dcbc9bed1ec438907ee.ttf │ │ │ ├── 718ec91358aa7ee8083b.ttf │ │ │ ├── 823d9b7475c12cab2754.ttf │ │ │ ├── 88fa7ae373b07b41ecce.ttf │ │ │ ├── 88fc88a1d67e79aaacde.ttf │ │ │ ├── 8e2a3df21501da2e09fa.ttf │ │ │ ├── a10812e5dfa71f8496d3.ttf │ │ │ ├── b1f4d2c69fa808ebca12.otf │ │ │ ├── b85d841dacb040b84951.otf │ │ │ ├── cac06450c6935ef24aa9.ttf │ │ │ ├── ce61e3623e6de15381db.ttf │ │ │ ├── d1f5e6e16dd4f75c3950.otf │ │ │ └── e89cb19905e7db5591b0.ttf │ ├── src │ │ ├── index.js │ │ ├── trackRotation.js │ │ ├── index.css │ │ └── modifyData.js │ ├── index.html │ └── webpack.common.js ├── compass │ ├── style.css │ ├── index.html │ └── index.js ├── main │ ├── tooltip.css │ ├── tempstream │ │ ├── style.css │ │ └── index.html │ ├── RPC.js │ ├── style.css │ ├── tool_buttons.css │ ├── popup.css │ ├── windows_section.css │ ├── layout_section.css │ ├── connection_section.css │ └── settings_section.css ├── tracktime │ ├── style.css │ ├── index.html │ └── index.js ├── scripts │ └── movemode.js ├── crashdetection │ ├── index.html │ ├── style.css │ └── index.js ├── styles │ └── window_info.css ├── battlemode │ ├── index.html │ └── style.css ├── currentlaps │ ├── index.html │ └── style.css ├── sessionlog │ ├── index.html │ └── style.css ├── functions │ ├── times.js │ ├── car.js │ ├── colors.js │ └── driver.js ├── singlercm │ ├── index.html │ ├── style.css │ └── index.js ├── autoswitch │ ├── style.css │ └── index.html ├── trackinfo │ ├── index.html │ ├── style.css │ └── index.js └── tirestats │ ├── style.css │ ├── index.js │ └── index.html ├── .github ├── ISSUE_TEMPLATE │ ├── feature-request.md │ └── bug_report.md └── workflows │ └── main.yml ├── store_manifest.json ├── electron-builder.yml ├── .gitignore ├── package.json └── readme.md /src/preload.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/fia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/fia.png -------------------------------------------------------------------------------- /src/icons/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/edit.png -------------------------------------------------------------------------------- /src/icons/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/save.png -------------------------------------------------------------------------------- /src/icons/compass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/compass.png -------------------------------------------------------------------------------- /src/icons/denied.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/denied.png -------------------------------------------------------------------------------- /src/icons/layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/layout.png -------------------------------------------------------------------------------- /src/icons/noicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/noicon.png -------------------------------------------------------------------------------- /src/icons/peanut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/peanut.png -------------------------------------------------------------------------------- /src/icons/restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/restore.png -------------------------------------------------------------------------------- /src/icons/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/settings.png -------------------------------------------------------------------------------- /src/fonts/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/Inter-Bold.ttf -------------------------------------------------------------------------------- /src/icons/arrowleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/arrowleft.png -------------------------------------------------------------------------------- /src/icons/arrowright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/arrowright.png -------------------------------------------------------------------------------- /src/icons/checkmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/checkmark.png -------------------------------------------------------------------------------- /src/icons/teams/haas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/haas.png -------------------------------------------------------------------------------- /src/icons/tires/hard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/hard.png -------------------------------------------------------------------------------- /src/icons/tires/soft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/soft.png -------------------------------------------------------------------------------- /src/icons/tires/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/test.png -------------------------------------------------------------------------------- /src/icons/tires/wet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/wet.png -------------------------------------------------------------------------------- /src/fonts/Formula1-Wide.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/Formula1-Wide.ttf -------------------------------------------------------------------------------- /src/fonts/Inter-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/Inter-Medium.ttf -------------------------------------------------------------------------------- /src/fonts/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/Inter-Regular.ttf -------------------------------------------------------------------------------- /src/icons/green-heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/green-heart.png -------------------------------------------------------------------------------- /src/icons/open-external.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/open-external.png -------------------------------------------------------------------------------- /src/icons/teams/alpine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/alpine.png -------------------------------------------------------------------------------- /src/icons/teams/ferrari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/ferrari.png -------------------------------------------------------------------------------- /src/icons/teams/mclaren.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/mclaren.png -------------------------------------------------------------------------------- /src/icons/teams/renault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/renault.png -------------------------------------------------------------------------------- /src/icons/tires/medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/medium.png -------------------------------------------------------------------------------- /src/icons/tires/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/unknown.png -------------------------------------------------------------------------------- /src/icons/unknowndriver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/unknowndriver.png -------------------------------------------------------------------------------- /src/icons/windows/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/color.png -------------------------------------------------------------------------------- /src/icons/windows/govee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/govee.png -------------------------------------------------------------------------------- /src/icons/windows/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/logo.png -------------------------------------------------------------------------------- /src/statuses/slippery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/statuses/slippery.png -------------------------------------------------------------------------------- /src/flagdisplay/Chequered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/flagdisplay/Chequered.png -------------------------------------------------------------------------------- /src/icons/messages/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/blank.png -------------------------------------------------------------------------------- /src/icons/teams/haas-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/haas-red.png -------------------------------------------------------------------------------- /src/icons/teams/mercedes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/mercedes.png -------------------------------------------------------------------------------- /src/icons/teams/red-bull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/red-bull.png -------------------------------------------------------------------------------- /src/icons/teams/williams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/williams.png -------------------------------------------------------------------------------- /src/icons/tires/hard_real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/hard_real.png -------------------------------------------------------------------------------- /src/icons/tires/soft_real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/soft_real.png -------------------------------------------------------------------------------- /src/icons/tires/test_real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/test_real.png -------------------------------------------------------------------------------- /src/icons/tires/wet_real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/wet_real.png -------------------------------------------------------------------------------- /src/icons/windows/compass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/compass.png -------------------------------------------------------------------------------- /src/icons/windows/weather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/weather.png -------------------------------------------------------------------------------- /src/fonts/TitilliumWeb-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/TitilliumWeb-Bold.ttf -------------------------------------------------------------------------------- /src/icons/messages/car_spun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/car_spun.png -------------------------------------------------------------------------------- /src/icons/messages/grip_low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/grip_low.png -------------------------------------------------------------------------------- /src/icons/messages/weather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/weather.png -------------------------------------------------------------------------------- /src/icons/teams/alfa-romeo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/alfa-romeo.png -------------------------------------------------------------------------------- /src/icons/teams/alpha-tauri.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/alpha-tauri.png -------------------------------------------------------------------------------- /src/icons/teams/aston-martin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/aston-martin.png -------------------------------------------------------------------------------- /src/icons/teams/force-india.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/force-india.png -------------------------------------------------------------------------------- /src/icons/teams/racing-point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/racing-point.png -------------------------------------------------------------------------------- /src/icons/teams/toro-rosso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/toro-rosso.png -------------------------------------------------------------------------------- /src/icons/tires/intermediate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/intermediate.png -------------------------------------------------------------------------------- /src/icons/tires/medium_real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/medium_real.png -------------------------------------------------------------------------------- /src/icons/tires/test_unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/test_unknown.png -------------------------------------------------------------------------------- /src/icons/windows/battlemode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/battlemode.png -------------------------------------------------------------------------------- /src/icons/windows/sessionlog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/sessionlog.png -------------------------------------------------------------------------------- /src/icons/windows/singlercm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/singlercm.png -------------------------------------------------------------------------------- /src/icons/windows/statuses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/statuses.png -------------------------------------------------------------------------------- /src/icons/windows/tirestats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/tirestats.png -------------------------------------------------------------------------------- /src/icons/windows/trackinfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/trackinfo.png -------------------------------------------------------------------------------- /src/icons/windows/tracktime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/tracktime.png -------------------------------------------------------------------------------- /src/fonts/Formula1-Bold_web_0.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/Formula1-Bold_web_0.ttf -------------------------------------------------------------------------------- /src/fonts/TitilliumWeb-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/TitilliumWeb-Italic.ttf -------------------------------------------------------------------------------- /src/fonts/TitilliumWeb-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/TitilliumWeb-Regular.ttf -------------------------------------------------------------------------------- /src/icons/flags/flag_chequered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/flags/flag_chequered.png -------------------------------------------------------------------------------- /src/icons/messages/car_stopped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/car_stopped.png -------------------------------------------------------------------------------- /src/icons/messages/correction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/correction.png -------------------------------------------------------------------------------- /src/icons/messages/drs_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/drs_enabled.png -------------------------------------------------------------------------------- /src/icons/messages/grip_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/grip_normal.png -------------------------------------------------------------------------------- /src/icons/teams/mclaren-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/mclaren-white.png -------------------------------------------------------------------------------- /src/icons/teams/williams-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/teams/williams-white.png -------------------------------------------------------------------------------- /src/icons/windows/currentlaps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/currentlaps.png -------------------------------------------------------------------------------- /src/icons/windows/flagdisplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/flagdisplay.png -------------------------------------------------------------------------------- /src/fonts/Exo2-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/Exo2-VariableFont_wght.ttf -------------------------------------------------------------------------------- /src/fonts/Formula1-Regular_web_0.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/Formula1-Regular_web_0.ttf -------------------------------------------------------------------------------- /src/fonts/TitilliumWeb-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/TitilliumWeb-BoldItalic.ttf -------------------------------------------------------------------------------- /src/fonts/TitilliumWeb-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/TitilliumWeb-SemiBold.ttf -------------------------------------------------------------------------------- /src/icons/messages/car_missedapex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/car_missedapex.png -------------------------------------------------------------------------------- /src/icons/messages/car_offtrack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/car_offtrack.png -------------------------------------------------------------------------------- /src/icons/messages/drs_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/drs_disabled.png -------------------------------------------------------------------------------- /src/icons/messages/grip_slippery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/grip_slippery.png -------------------------------------------------------------------------------- /src/icons/messages/incident_noted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/incident_noted.png -------------------------------------------------------------------------------- /src/icons/messages/penalty_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/penalty_time.png -------------------------------------------------------------------------------- /src/icons/messages/pit_entry_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/pit_entry_open.png -------------------------------------------------------------------------------- /src/icons/messages/pit_exit_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/pit_exit_open.png -------------------------------------------------------------------------------- /src/icons/messages/session_resume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/session_resume.png -------------------------------------------------------------------------------- /src/icons/tires/intermediate_real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/tires/intermediate_real.png -------------------------------------------------------------------------------- /src/icons/windows/crashdetection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/windows/crashdetection.png -------------------------------------------------------------------------------- /src/icons/flags/flag_blackandorange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/flags/flag_blackandorange.png -------------------------------------------------------------------------------- /src/icons/flags/flag_blackandwhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/flags/flag_blackandwhite.png -------------------------------------------------------------------------------- /src/icons/messages/car_tracklimits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/car_tracklimits.png -------------------------------------------------------------------------------- /src/icons/messages/pit_entry_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/pit_entry_closed.png -------------------------------------------------------------------------------- /src/icons/messages/pit_exit_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/pit_exit_closed.png -------------------------------------------------------------------------------- /src/fonts/Formula1-Bold_web_0_modified.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/Formula1-Bold_web_0_modified.ttf -------------------------------------------------------------------------------- /src/fonts/TitilliumWeb-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/fonts/TitilliumWeb-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /src/icons/messages/penalty_stopandgo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/penalty_stopandgo.png -------------------------------------------------------------------------------- /src/icons/messages/safetycar_pitlane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/safetycar_pitlane.png -------------------------------------------------------------------------------- /src/icons/messages/safetycar_startfinish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/safetycar_startfinish.png -------------------------------------------------------------------------------- /src/icons/messages/session_startdelayed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/session_startdelayed.png -------------------------------------------------------------------------------- /src/icons/messages/session_durationchanged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/session_durationchanged.png -------------------------------------------------------------------------------- /src/weather/build/js/20b52d0c6ea89997befb.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/20b52d0c6ea89997befb.ttf -------------------------------------------------------------------------------- /src/weather/build/js/425b19b61e022e504f51.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/425b19b61e022e504f51.ttf -------------------------------------------------------------------------------- /src/weather/build/js/50c688fdc8d382c66303.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/50c688fdc8d382c66303.ttf -------------------------------------------------------------------------------- /src/weather/build/js/6dcbc9bed1ec438907ee.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/6dcbc9bed1ec438907ee.ttf -------------------------------------------------------------------------------- /src/weather/build/js/718ec91358aa7ee8083b.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/718ec91358aa7ee8083b.ttf -------------------------------------------------------------------------------- /src/weather/build/js/823d9b7475c12cab2754.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/823d9b7475c12cab2754.ttf -------------------------------------------------------------------------------- /src/weather/build/js/88fa7ae373b07b41ecce.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/88fa7ae373b07b41ecce.ttf -------------------------------------------------------------------------------- /src/weather/build/js/88fc88a1d67e79aaacde.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/88fc88a1d67e79aaacde.ttf -------------------------------------------------------------------------------- /src/weather/build/js/8e2a3df21501da2e09fa.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/8e2a3df21501da2e09fa.ttf -------------------------------------------------------------------------------- /src/weather/build/js/a10812e5dfa71f8496d3.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/a10812e5dfa71f8496d3.ttf -------------------------------------------------------------------------------- /src/weather/build/js/b1f4d2c69fa808ebca12.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/b1f4d2c69fa808ebca12.otf -------------------------------------------------------------------------------- /src/weather/build/js/b85d841dacb040b84951.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/b85d841dacb040b84951.otf -------------------------------------------------------------------------------- /src/weather/build/js/cac06450c6935ef24aa9.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/cac06450c6935ef24aa9.ttf -------------------------------------------------------------------------------- /src/weather/build/js/ce61e3623e6de15381db.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/ce61e3623e6de15381db.ttf -------------------------------------------------------------------------------- /src/weather/build/js/d1f5e6e16dd4f75c3950.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/d1f5e6e16dd4f75c3950.otf -------------------------------------------------------------------------------- /src/weather/build/js/e89cb19905e7db5591b0.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/weather/build/js/e89cb19905e7db5591b0.ttf -------------------------------------------------------------------------------- /src/icons/messages/incident_nofurther_action.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/incident_nofurther_action.png -------------------------------------------------------------------------------- /src/icons/messages/incident_underinvestigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/incident_underinvestigation.png -------------------------------------------------------------------------------- /src/icons/messages/incident_nofurther_investigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/incident_nofurther_investigation.png -------------------------------------------------------------------------------- /src/icons/messages/incident_investigationafterthesession.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRAJEKO/UF1-Viewer-with-F1MV/HEAD/src/icons/messages/incident_investigationafterthesession.png -------------------------------------------------------------------------------- /src/compass/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | overflow: hidden; 5 | } 6 | 7 | #compass { 8 | width: calc(100vw); 9 | } 10 | 11 | .drag { 12 | -webkit-app-region: drag; 13 | } 14 | -------------------------------------------------------------------------------- /src/weather/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | 4 | import Graph from "./graph"; 5 | 6 | import "../../fonts/fonts.css"; 7 | 8 | import "../../styles/window_info.css"; 9 | 10 | import "./index.css"; 11 | 12 | const rootElement = document.getElementById("root"); 13 | const root = createRoot(rootElement); 14 | root.render( 15 |
00:00:00
14 |This window is currently in move mode. You can press 'Escape' to toggle it and switch between moving and functionality
When the live session starts, your MultiViewer window will automatically open.
15 |0
17 |25 | Important: This window may never be minimized and should always be on top in order for it to 26 | function properly hiding it will not disable the script. You can press 'Hide' to make the window 27 | fully transparent. Press 'Escape' to exit the 'hidden' state. 28 |
29 |ONSCHEDULE
20 |12:34:56
21 | 22 | 23 |12:34:56
25 |12:34:56
26 | 27 | 28 |43% - 56%
30 |56/93
31 |Q1/Q3
32 | 33 | 34 |NORMAL
36 | 37 | 38 |PINK
40 | 41 | 42 |ENABLED
44 | 45 | 46 |NONE
48 | 49 | 50 |ENTRY
52 |EXIT
53 | 54 |CLEAR
`; 38 | container.innerHTML += newStatus; 39 | } 40 | } 41 | 42 | function setFullStatus() { 43 | const status = parseInt(trackStatus.Status); 44 | 45 | let message = "TRACK CLEAR"; 46 | let color = "green"; 47 | switch (status) { 48 | case 2: 49 | message = "YELLOW FLAG"; 50 | color = "yellow"; 51 | break; 52 | case 4: 53 | message = "SC DEPLOYED"; 54 | color = "yellow"; 55 | break; 56 | case 5: 57 | message = "RED FLAG"; 58 | color = "red"; 59 | break; 60 | case 6: 61 | message = "VSC DEPLOYED"; 62 | color = "yellow"; 63 | break; 64 | case 7: 65 | message = "VSC ENDING"; 66 | color = "yellow"; 67 | break; 68 | } 69 | 70 | const fullStatusElement = document.getElementById("track-status"); 71 | 72 | fullStatusElement.textContent = message; 73 | fullStatusElement.className = color; 74 | } 75 | 76 | let pastRaceControlMessages = []; 77 | function setTrackSector() { 78 | for (const message of raceControlMessages) { 79 | if (pastRaceControlMessages.includes(JSON.stringify(message))) continue; 80 | 81 | pastRaceControlMessages.push(JSON.stringify(message)); 82 | 83 | if (message.Category === "Flag" && message.Scope === "Track" && message.Flag === "CLEAR") { 84 | const allSectorElements = document.getElementsByClassName("status"); 85 | 86 | for (const sectorElement of allSectorElements) { 87 | sectorElement.children[1].textContent = "CLEAR"; 88 | sectorElement.children[1].className = "green"; 89 | } 90 | } 91 | 92 | if ( 93 | (message.Category !== "Flag" || message.Scope !== "Sector") && 94 | message.SubCategory !== "TrackSurfaceSlippery" 95 | ) 96 | continue; 97 | 98 | let sector = message.Sector; 99 | 100 | let flag = message.Flag; 101 | 102 | if (sector === undefined) { 103 | sector = message.Message.match(/(\d+)/)[0]; 104 | flag = "SLIPPERY"; 105 | } 106 | 107 | let color = "green"; 108 | switch (flag) { 109 | case "YELLOW": 110 | color = "yellow"; 111 | break; 112 | case "DOUBLE YELLOW": 113 | color = "orange"; 114 | break; 115 | case "SLIPPERY": 116 | color = "slippery"; 117 | } 118 | 119 | const sectorElement = document.getElementById(`sector${sector}`); 120 | sectorElement.textContent = flag; 121 | sectorElement.className = color; 122 | } 123 | } 124 | 125 | async function run() { 126 | await getConfigurations(); 127 | await apiRequests(); 128 | await addTrackSectors(); 129 | setInterval(async () => { 130 | await apiRequests(); 131 | setFullStatus(); 132 | setTrackSector(); 133 | }, 500); 134 | } 135 | 136 | run(); 137 | 138 | // {"Status":"1","Message":"AllClear"} 139 | // {"Status":"2","Message":"Yellow"} 140 | // event 3 has never been seen 141 | // {"Status":"4","Message":"SCDeployed"} 142 | // {"Status":"5","Message":"Red"} 143 | // {"Status":"6","Message":"VSCDeployed"} 144 | // {"Status":"7","Message":"VSCEnding"} 145 | -------------------------------------------------------------------------------- /src/currentlaps/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | body { 7 | height: 100vh; 8 | width: 100vw; 9 | overflow: hidden; 10 | } 11 | 12 | .drag { 13 | -webkit-app-region: drag; 14 | } 15 | 16 | /* Container and animations */ 17 | 18 | .window_header { 19 | padding: 5vw; 20 | } 21 | 22 | .window_header h1 { 23 | font-size: 8vw; 24 | color: white; 25 | font-family: "SFBold"; 26 | } 27 | 28 | .line { 29 | height: 2px; 30 | width: 100%; 31 | border-radius: 5vw; 32 | background-color: #0a0a0faf; 33 | margin: 3vw 0; 34 | } 35 | 36 | .wrapper { 37 | background-color: #0a0a0faf; 38 | border-radius: 6vw; 39 | text-align: center; 40 | margin-bottom: 3vw; 41 | } 42 | 43 | #container li { 44 | height: 0; 45 | margin-bottom: 0; 46 | overflow: hidden; 47 | transition: all 0.5s ease-in-out; 48 | border: none; 49 | } 50 | 51 | #container li.show { 52 | height: 37vw !important; 53 | margin-bottom: 5px; 54 | } 55 | 56 | #container li.show.highlight { 57 | border-left: 1.5vw solid rgb(255, 251, 0); 58 | } 59 | 60 | #container li.show.highlight .header { 61 | border-left: 0.5vw solid black; 62 | } 63 | 64 | /* General time styling */ 65 | .improved-time, 66 | .pushlap { 67 | background-color: #0a0a0f; 68 | margin-bottom: 2vw; 69 | } 70 | 71 | /* Header styling */ 72 | .header { 73 | display: flex; 74 | background-color: white; 75 | color: black; 76 | height: 10vw; 77 | width: 100%; 78 | } 79 | 80 | .danger { 81 | background-color: #b60000 !important; 82 | color: white !important; 83 | } 84 | 85 | .icon { 86 | display: flex; 87 | justify-content: center; 88 | align-items: center; 89 | height: 10vw; 90 | width: 10vw; 91 | } 92 | 93 | .icon img { 94 | height: 9vw; 95 | } 96 | 97 | .position { 98 | width: 10vw; 99 | height: 10vw; 100 | display: flex; 101 | justify-content: center; 102 | align-items: center; 103 | border-right: 1px solid #2d2d33; 104 | } 105 | 106 | .position p { 107 | font-family: "OF1Regular", Arial, Helvetica, sans-serif; 108 | transform: skew(-10deg); 109 | font-size: 5vw; 110 | width: 5vw; 111 | display: flex; 112 | align-items: center; 113 | justify-content: center; 114 | } 115 | 116 | .name { 117 | width: 70vw; 118 | height: 10vw; 119 | display: flex; 120 | align-items: center; 121 | } 122 | 123 | .name p { 124 | font-size: 6vw; 125 | padding-left: 2vw; 126 | } 127 | 128 | .tire { 129 | display: flex; 130 | justify-content: center; 131 | align-items: center; 132 | width: 10vw; 133 | height: 10vw; 134 | float: right; 135 | background-color: #00000a; 136 | } 137 | 138 | .tire p { 139 | font-family: "OF1Regular", Arial, Helvetica, sans-serif; 140 | transform: skew(0deg); 141 | font-size: 4.5vw; 142 | } 143 | 144 | /* Time styling */ 145 | .times { 146 | height: 16vw; 147 | display: flex; 148 | } 149 | 150 | .personal, 151 | .target { 152 | width: 50vw; 153 | } 154 | 155 | .personal { 156 | border-right: 0.1vw solid #2d2d33; 157 | display: flex; 158 | justify-content: center; 159 | align-items: center; 160 | } 161 | 162 | .personal p { 163 | font-size: 8.5vw; 164 | } 165 | 166 | .target { 167 | border-left: 0.1vw solid #2d2d33; 168 | gap: 3vw; 169 | display: flex; 170 | justify-content: center; 171 | align-items: center; 172 | color: white; 173 | } 174 | 175 | .target p { 176 | font-size: 4vw; 177 | } 178 | 179 | .target-name p { 180 | font-size: 3vw; 181 | color: #5b5b5d; 182 | } 183 | 184 | .top, 185 | .bottom { 186 | height: 100%; 187 | display: flex; 188 | flex-direction: column; 189 | justify-content: space-evenly; 190 | align-items: center; 191 | } 192 | 193 | /* Sector styling */ 194 | .sectors { 195 | border-top: 0.2vw solid #2d2d33; 196 | display: flex; 197 | justify-content: space-between; 198 | align-items: flex-end; 199 | } 200 | 201 | .sector { 202 | width: 100%; 203 | padding: 0 1vw 1vw; 204 | } 205 | 206 | #sector1 { 207 | border-left: 0.1vw solid #2d2d33; 208 | border-right: 0.1vw solid #2d2d33; 209 | } 210 | 211 | .sector-times { 212 | display: flex; 213 | justify-content: space-evenly; 214 | } 215 | 216 | .sector-time { 217 | display: flex; 218 | align-items: center; 219 | justify-content: center; 220 | text-align: center; 221 | color: white; 222 | font-size: 4.5vw; 223 | margin: 1.5vw 0; 224 | } 225 | 226 | .sector-delta { 227 | display: flex; 228 | align-items: center; 229 | justify-content: center; 230 | text-align: center; 231 | color: white; 232 | font-size: 3vw; 233 | } 234 | 235 | .sector-color { 236 | height: 1vw; 237 | } 238 | 239 | .sectors p { 240 | font-family: "OF1Regular"; 241 | } 242 | 243 | p { 244 | font-family: "OF1Bold"; 245 | transform: skew(-10deg); 246 | } 247 | 248 | /* New styling */ 249 | .segments { 250 | display: flex; 251 | width: 100%; 252 | height: 1vw; 253 | gap: 0.4vw; 254 | } 255 | 256 | .segment { 257 | background-color: #5b5b5d; 258 | width: 100%; 259 | } 260 | -------------------------------------------------------------------------------- /src/functions/driver.js: -------------------------------------------------------------------------------- 1 | const { parseLapOrSectorTime } = require("./times"); 2 | 3 | // Check if the driver is on a push lap or not 4 | function isDriverOnPushLap(sessionStatus, trackStatus, timingData, bestTimes, sessionType, driverNumber) { 5 | if (sessionStatus === "Aborted" || sessionStatus === "Inactive" || [4, 5, 6, 7].includes(trackStatus)) return false; 6 | 7 | const driverTimingData = timingData[driverNumber]; 8 | const driverBestTimes = bestTimes[driverNumber]; 9 | 10 | if (sessionType === "Race" && (driverTimingData.NumberOfLaps === undefined || driverTimingData.NumberOfLaps <= 1)) 11 | return false; 12 | 13 | if (driverTimingData.InPit) return false; 14 | 15 | // If the first mini sector time is status 2064, meaning he is on a out lap, return false 16 | if (driverTimingData.Sectors[0].Segments?.[0].Status === 2064) return false; 17 | 18 | // Get the threshold to which the sector time should be compared to the best personal sector time. 19 | const pushDeltaThreshold = sessionType === "Race" ? 0.2 : sessionType === "Qualifying" ? 1 : 3; 20 | 21 | const sectors = driverTimingData.Sectors; 22 | 23 | const lastSector = sectors.slice(-1)[0]; 24 | 25 | if (sectors.slice(-1)[0].Value !== "" && (sectors.slice(-1)[0].Segments?.slice(-1)[0].Status !== 0 ?? true)) 26 | return false; 27 | 28 | const completedFirstSector = sectors[0].Segments 29 | ? (sectors[0].Segments.slice(-1)[0].Status !== 0 && lastSector.Value === "") || 30 | (lastSector.Segments.slice(-1)[0].Status === 0 && 31 | lastSector.Value !== "" && 32 | sectors[1].Segments[0].Status !== 0 && 33 | sectors[0].Segments.slice(-1)[0].Status !== 0) 34 | : sectors[0].Value !== 0 && lastSector.Value === ""; 35 | 36 | let isPushing = false; 37 | for (let sectorIndex = 0; sectorIndex < driverTimingData.Sectors.length; sectorIndex++) { 38 | const sector = sectors[sectorIndex]; 39 | const bestSector = driverBestTimes.BestSectors[sectorIndex]; 40 | 41 | const sectorTime = parseLapOrSectorTime(sector.Value); 42 | const bestSectorTime = parseLapOrSectorTime(bestSector.Value); 43 | 44 | // Check if the first sector is completed by checking if the last segment of the first sector has a value meaning he has crossed the last point of that sector and the final sector time does not have a value. The last check is done because sometimes the segment already has a status but the times are not updated yet. 45 | 46 | // If the first sector time is above the threshold it should imidiately break because it will not be a push lap 47 | if (sectorTime - bestSectorTime > pushDeltaThreshold && completedFirstSector) { 48 | isPushing = false; 49 | break; 50 | } 51 | 52 | // If the first sector time is lower then the threshold it should temporarily set pushing to true because the driver could have still backed out in a later stage 53 | if (sectorTime - bestSectorTime <= pushDeltaThreshold && completedFirstSector) { 54 | isPushing = true; 55 | continue; 56 | } 57 | 58 | // If the driver has a fastest segment overall it would temporarily set pushing to true because the driver could have still backed out in a later stage 59 | if (sector.Segments?.some((segment) => segment.Status === 2051) && sessionType !== "Race") { 60 | isPushing = true; 61 | continue; 62 | } 63 | } 64 | 65 | // Return the final pushing state 66 | return isPushing; 67 | } 68 | 69 | // Get the position of the driver based on their segments 70 | function getDriverPosition(driverNumber, timingData) { 71 | const driverTimingData = timingData[driverNumber]; 72 | const sectors = driverTimingData.Sectors; 73 | 74 | // The starting segment will always be 0 because if there is no 0 state anywhere all segments will be completed and the current segment will be the first one. 75 | let currentSegment = -1; 76 | 77 | const driverCount = Object.keys(timingData).length; 78 | 79 | if (sectors[0].Segments) { 80 | for (const sectorIndex in sectors) { 81 | const segments = sectors[sectorIndex].Segments; 82 | for (const segmentIndex in segments) { 83 | const segment = segments[segmentIndex]; 84 | if (segment.Status === 0) return parseInt(currentSegment) + parseInt(segmentIndex); 85 | } 86 | currentSegment += segments.length; 87 | } 88 | } else { 89 | currentSegment = driverCount - parseInt(driverTimingData.Position); 90 | } 91 | 92 | const lastSectorValue = sectors.slice(-1)[0].Value; 93 | 94 | if (lastSectorValue === "") return currentSegment; 95 | 96 | return 0; 97 | } 98 | 99 | function getDriversTrackOrder(timingData) { 100 | const driverOrder = Object.keys(timingData).sort((a, b) => { 101 | const positionDriverA = getDriverPosition(a, timingData); 102 | const positionDriverB = getDriverPosition(b, timingData); 103 | 104 | if (positionDriverA === positionDriverB) return timingData[a].Position - timingData[b].Position; 105 | 106 | return positionDriverB - positionDriverA; 107 | }); 108 | 109 | return driverOrder; 110 | } 111 | 112 | module.exports = { 113 | isDriverOnPushLap, 114 | getDriverPosition, 115 | getDriversTrackOrder, 116 | }; 117 | -------------------------------------------------------------------------------- /src/battlemode/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | color: white; 5 | } 6 | 7 | body { 8 | height: 100vh; 9 | width: 100vw; 10 | overflow: hidden; 11 | } 12 | 13 | .drag { 14 | -webkit-app-region: drag; 15 | } 16 | 17 | .transparent { 18 | background-color: transparent !important; 19 | } 20 | 21 | #container { 22 | height: 100vh; 23 | width: 100vw; 24 | } 25 | 26 | #wrapper { 27 | background-color: #0a0a0faf; 28 | border-radius: 25px; 29 | padding: 4vh 8vh; 30 | height: calc(55% - 8vh); 31 | width: calc(100% - 16vh); 32 | display: flex; 33 | justify-content: space-between; 34 | position: absolute; 35 | bottom: 20vh; 36 | } 37 | 38 | .driver { 39 | height: 100%; 40 | display: flex; 41 | gap: 7vh; 42 | } 43 | 44 | .driver-info { 45 | position: relative; 46 | top: calc(-40% - 3.5vh); 47 | height: calc(140% + 3.5vh); 48 | width: 65vh; 49 | } 50 | 51 | .driver-headshot { 52 | height: 70%; 53 | position: relative; 54 | border-bottom: 2px solid white; 55 | display: flex; 56 | justify-content: center; 57 | } 58 | 59 | .driver-headshot img { 60 | position: absolute; 61 | bottom: 0; 62 | height: 110%; 63 | } 64 | 65 | .driver-name { 66 | padding: 2vh 5vh; 67 | } 68 | 69 | .driver-name .first-name { 70 | padding-left: 1vh; 71 | font-family: "OF1Regular"; 72 | font-size: 5vh; 73 | transform: skew(-10deg); 74 | text-shadow: 0.5vh 0.5vh 0.5vh black; 75 | } 76 | 77 | .driver-name .last-name { 78 | font-family: "OF1Bold"; 79 | font-size: 7vh; 80 | transform: skew(-10deg); 81 | text-shadow: 0.5vh 0.5vh 0.5vh black; 82 | } 83 | 84 | .pitlane { 85 | padding: 1vh 0; 86 | display: flex; 87 | flex-direction: column; 88 | align-items: center; 89 | justify-content: space-between; 90 | } 91 | 92 | .position p { 93 | font-family: "OF1Bold"; 94 | font-size: 6vh; 95 | transform: skew(-10deg); 96 | } 97 | 98 | .tires { 99 | width: 10vh; 100 | display: flex; 101 | flex-direction: column; 102 | align-items: center; 103 | justify-content: center; 104 | } 105 | 106 | .tires .current-tire img { 107 | width: 100%; 108 | } 109 | 110 | .tire-age { 111 | font-family: "SFBold"; 112 | font-size: 7vh; 113 | } 114 | 115 | .pit { 116 | width: 100%; 117 | display: grid; 118 | place-items: center; 119 | } 120 | 121 | .pit p { 122 | font-family: "OF1Bold"; 123 | border: 2px solid red; 124 | color: red; 125 | width: 10vh; 126 | padding: 1vh; 127 | border-radius: 3vh; 128 | display: grid; 129 | place-items: center; 130 | font-size: 6vh; 131 | } 132 | 133 | .times { 134 | display: flex; 135 | flex-direction: column; 136 | justify-content: space-around; 137 | width: 40vh; 138 | } 139 | 140 | .time-name { 141 | font-family: "SFRegular"; 142 | font-size: 7vh; 143 | } 144 | 145 | .time-time { 146 | font-family: "SFBold"; 147 | font-size: 9vh; 148 | } 149 | 150 | .telemetry { 151 | display: flex; 152 | flex-direction: column; 153 | align-items: center; 154 | justify-content: space-around; 155 | width: 50vh; 156 | } 157 | 158 | .drs p { 159 | padding: 1vh 5vh; 160 | width: fit-content; 161 | font-size: 8vh; 162 | border-radius: 2vh; 163 | font-family: "OF1Bold"; 164 | text-align: center; 165 | border: 2px solid green; 166 | color: green; 167 | } 168 | 169 | .off { 170 | opacity: 0.3; 171 | } 172 | 173 | .speed { 174 | display: flex; 175 | gap: 2vh; 176 | } 177 | 178 | .speed-number { 179 | font-family: "OF1Bold"; 180 | font-size: 8vh; 181 | } 182 | 183 | .metric { 184 | font-family: "OF1Regular"; 185 | font-size: 5vh; 186 | top: 3vh; 187 | position: relative; 188 | } 189 | 190 | .pedals { 191 | height: 9vh; 192 | display: flex; 193 | flex-direction: column; 194 | justify-content: space-between; 195 | width: 80%; 196 | } 197 | 198 | .pedal { 199 | border: 0.5vh solid black; 200 | height: 2vh; 201 | border-radius: 4vh; 202 | overflow: hidden; 203 | } 204 | 205 | .pedal div { 206 | width: 0; 207 | height: 100%; 208 | transition: all 0.2s ease; 209 | } 210 | 211 | .gap { 212 | width: 100%; 213 | display: flex; 214 | align-items: center; 215 | gap: 10vh; 216 | padding: 0 10vh; 217 | } 218 | 219 | .gap .line { 220 | width: 100%; 221 | border-bottom: 1vh dashed white; 222 | } 223 | 224 | .gap .gap-time { 225 | display: flex; 226 | flex-direction: column; 227 | align-items: center; 228 | justify-content: center; 229 | } 230 | 231 | .gap .gap-time-time { 232 | font-family: "OF1Bold"; 233 | white-space: nowrap; 234 | font-size: 10vh; 235 | transform: skew(-10deg); 236 | } 237 | 238 | .gap .gap-time-format { 239 | font-family: "OF1Regular"; 240 | font-size: 6vh; 241 | } 242 | 243 | .shown { 244 | opacity: 1 !important; 245 | } 246 | 247 | #buttons { 248 | position: absolute; 249 | bottom: 0; 250 | left: 0; 251 | width: 100vw; 252 | background-color: #0a0a0faf; 253 | height: 18vh; 254 | border-radius: 5vh; 255 | opacity: 0; 256 | transition: all 0.5s ease; 257 | display: flex; 258 | justify-content: space-evenly; 259 | gap: 2vh; 260 | align-items: center; 261 | } 262 | 263 | .button { 264 | width: 24vh; 265 | height: 14vh; 266 | background-color: transparent; 267 | border-radius: 5vh; 268 | display: grid; 269 | place-items: center; 270 | border: 0.5vh solid; 271 | opacity: 0.6; 272 | outline: none; 273 | transition: all 0.5s ease; 274 | font-family: "SFBold"; 275 | font-size: 7vh; 276 | } 277 | 278 | .button:hover { 279 | opacity: 1; 280 | } 281 | 282 | .green { 283 | background-color: green; 284 | } 285 | 286 | .fastest-time { 287 | color: #9c27b0; 288 | } 289 | 290 | .personal-best { 291 | color: #4caf50; 292 | } 293 | 294 | .red-text { 295 | color: red; 296 | } 297 | 298 | .slow { 299 | color: #fdd835; 300 | } 301 | 302 | .red { 303 | background-color: red; 304 | } 305 | 306 | .gray { 307 | color: rgba(255, 255, 255, 0.233); 308 | } 309 | -------------------------------------------------------------------------------- /src/flagdisplay/index.js: -------------------------------------------------------------------------------- 1 | const debug = false; 2 | 3 | const f1mvApi = require("npm_f1mv_api"); 4 | 5 | const { ipcRenderer } = require("electron"); 6 | 7 | let goveeConnected = false; 8 | let goveeDevices = []; 9 | 10 | async function goveeHandler() { 11 | const goveeEnabled = await (async () => (await ipcRenderer.invoke("get_store")).config.flag_display.govee)(); 12 | if (goveeEnabled) { 13 | if (await ipcRenderer.invoke("checkGoveeWindowExistence")) return; 14 | 15 | const Govee = require("govee-lan-control"); 16 | const govee = new Govee.default(); 17 | 18 | async function showGoveeWindow() { 19 | const goveePanel = window.open( 20 | "govee/index.html", 21 | "_blank", 22 | `width=150,height=150,frame=false,transparent=true,hideMenuBar=true,hasShadow=false,alwaysOnTop=true,movable=false,resizable=false` 23 | ); 24 | 25 | await sleep(100); 26 | 27 | goveePanel.document.getElementById("connected").textContent = goveeDevices.length; 28 | 29 | await sleep(5000); 30 | 31 | goveePanel.document.getElementById("container").classList.add("hide"); 32 | 33 | await sleep(500); 34 | 35 | goveePanel.close(); 36 | } 37 | 38 | govee.on("deviceAdded", async (device) => { 39 | console.log("Connected to Govee device: " + device.model); 40 | 41 | goveeDevices.push(device); 42 | 43 | setGoveeLight("green"); 44 | 45 | await sleep(1000); 46 | 47 | setGoveeLight("default"); 48 | 49 | goveeConnected = true; 50 | 51 | await showGoveeWindow(); 52 | }); 53 | 54 | govee.on("deviceRemoved", async (device) => { 55 | console.log("Govee device disconnected: " + device.model); 56 | const deviceIndex = goveeDevices.indexOf(device); 57 | 58 | goveeDevices.splice(deviceIndex, 1); 59 | 60 | if (goveeDevices.length === 0) goveeConnected = false; 61 | 62 | await showGoveeWindow(); 63 | }); 64 | } 65 | } 66 | 67 | // Set sleep 68 | const sleep = (milliseconds) => { 69 | return new Promise((resolve) => setTimeout(resolve, milliseconds)); 70 | }; 71 | 72 | // Set basic info 73 | const flag = document.getElementById("flag"); 74 | const extra = document.getElementById("extra"); 75 | const chequered = document.getElementById("chequered"); 76 | 77 | async function getConfigurations() { 78 | const configfile = (await ipcRenderer.invoke("get_store")).config; 79 | host = configfile.network.host; 80 | port = (await f1mvApi.discoverF1MVInstances(host)).port; 81 | config = { 82 | host: host, 83 | port: port, 84 | }; 85 | if (debug) { 86 | console.log(host); 87 | console.log(port); 88 | } 89 | } 90 | 91 | async function setGoveeLight(color) { 92 | const ledColors = (await ipcRenderer.invoke("get_store")).led_colors; 93 | const rgbColor = ledColors[color]; 94 | 95 | console.log("Set govee light to: " + color); 96 | 97 | console.log(goveeDevices); 98 | for (const device of goveeDevices) { 99 | await device.actions.fadeColor({ 100 | time: 500, 101 | color: { 102 | rgb: rgbColor, 103 | }, 104 | brightness: 100, 105 | }); 106 | } 107 | } 108 | 109 | let prevTrackStatus; 110 | let prevSessionStatus; 111 | let prevFastestLap; 112 | async function getCurrentStatus() { 113 | const data = await f1mvApi.LiveTimingAPIGraphQL(config, ["TrackStatus", "SessionStatus", "TimingData"]); 114 | 115 | const trackStatus = parseInt(data.TrackStatus.Status); 116 | if (trackStatus !== prevTrackStatus) { 117 | prevTrackStatus = trackStatus; 118 | switch (trackStatus) { 119 | case 1: 120 | if (goveeConnected) setGoveeLight("green"); 121 | flag.classList.remove("red"); 122 | flag.classList.remove("yellow"); 123 | flag.classList.add("green"); 124 | await sleep(5000); 125 | if (goveeConnected) setGoveeLight("default"); 126 | flag.classList.remove("green"); 127 | break; 128 | case 2: 129 | if (goveeConnected) setGoveeLight("yellow"); 130 | flag.classList.add("yellow"); 131 | break; 132 | case 4: 133 | await blink("yellow", 3, 500); 134 | if (goveeConnected) setGoveeLight("yellow"); 135 | flag.classList.add("yellow"); 136 | break; 137 | case 5: 138 | if (goveeConnected) setGoveeLight("red"); 139 | flag.classList.remove("yellow"); 140 | flag.classList.add("red"); 141 | break; 142 | case 6: 143 | case 7: 144 | await blink("yellow", 3, 750); 145 | if (goveeConnected) setGoveeLight("yellow"); 146 | flag.classList.add("yellow"); 147 | break; 148 | } 149 | } 150 | 151 | const sessionStatus = data.SessionStatus.Status; 152 | if ((sessionStatus === "Finished" || sessionStatus === "Finalised") && prevSessionStatus !== sessionStatus) { 153 | prevSessionStatus = sessionStatus; 154 | await finishBlink("white", 5, 1000); 155 | if (goveeConnected) setGoveeLight("default"); 156 | } 157 | 158 | console.log(trackStatus); 159 | 160 | const timingData = data.TimingData.Lines; 161 | for (const driverNumber in timingData) { 162 | const driverTimingData = timingData[driverNumber]; 163 | 164 | const driverFastestLap = driverTimingData.LastLapTime.OverallFastest; 165 | const driverFastestLapTime = driverTimingData.LastLapTime.Value; 166 | 167 | if (driverFastestLap) { 168 | console.log(driverFastestLap && driverFastestLapTime !== prevFastestLap); 169 | } 170 | 171 | if (driverFastestLap && driverFastestLapTime !== prevFastestLap) { 172 | prevFastestLap = driverFastestLapTime; 173 | if (goveeConnected) setGoveeLight("purple"); 174 | extra.classList.add("fastest-lap"); 175 | await sleep(3000); 176 | if (goveeConnected) setGoveeLight("default"); 177 | extra.classList.remove("fastest-lap"); 178 | } 179 | } 180 | } 181 | 182 | async function finishBlink(color, amount, interval) { 183 | for (let count = 0; count < amount; count++) { 184 | if (goveeConnected) setGoveeLight(color); 185 | chequered.classList.add(color); 186 | await sleep(interval); 187 | if (goveeConnected) setGoveeLight("black"); 188 | chequered.classList.remove(color); 189 | await sleep(interval); 190 | } 191 | } 192 | 193 | async function blink(color, amount, interval) { 194 | for (let count = 0; count < amount; count++) { 195 | if (goveeConnected) setGoveeLight(color); 196 | flag.classList.add(color); 197 | await sleep(interval); 198 | if (goveeConnected) setGoveeLight("black"); 199 | flag.classList.remove(color); 200 | await sleep(interval); 201 | } 202 | } 203 | 204 | async function run() { 205 | await goveeHandler(); 206 | await getConfigurations(); 207 | 208 | while (true) { 209 | await getCurrentStatus(); 210 | await sleep(250); 211 | } 212 | } 213 | 214 | run(); 215 | 216 | // {"Status":"1","Message":"AllClear"} 217 | // {"Status":"2","Message":"Yellow"} 218 | // {Status: "3", Message: ""} 219 | // {Status: "4", Message: "SCDeployed"} 220 | // {"Status":"5","Message":"Red"} 221 | // {"Status":"6","Message":"VSCDeployed"} 222 | // {"Status":"7","Message":"VSCEnding"} 223 | -------------------------------------------------------------------------------- /src/crashdetection/index.js: -------------------------------------------------------------------------------- 1 | const debug = false; 2 | 3 | const { ipcRenderer } = require("electron"); 4 | 5 | const f1mvApi = require("npm_f1mv_api"); 6 | 7 | // Set sleep 8 | const sleep = (milliseconds) => { 9 | return new Promise((resolve) => setTimeout(resolve, milliseconds)); 10 | }; 11 | 12 | let crashCount = 0; 13 | async function getConfigurations() { 14 | const configFile = (await ipcRenderer.invoke("get_store")).config; 15 | host = configFile.network.host; 16 | port = (await f1mvApi.discoverF1MVInstances(host)).port; 17 | if (debug) { 18 | console.log(host); 19 | console.log(port); 20 | } 21 | } 22 | 23 | async function apiRequests() { 24 | const config = { 25 | host: host, 26 | port: port, 27 | }; 28 | 29 | const liveTimingState = await f1mvApi.LiveTimingAPIGraphQL(config, [ 30 | "DriverList", 31 | "CarData", 32 | "TimingData", 33 | "SessionStatus", 34 | "SessionInfo", 35 | "TrackStatus", 36 | "LapCount", 37 | ]); 38 | driverList = liveTimingState.DriverList; 39 | carData = liveTimingState.CarData.Entries; 40 | timingData = liveTimingState.TimingData.Lines; 41 | sessionStatus = liveTimingState.SessionStatus.Status; 42 | sessionInfo = liveTimingState.SessionInfo; 43 | sessionType = sessionInfo.Type; 44 | trackStatus = liveTimingState.TrackStatus; 45 | if (sessionType === "Race") { 46 | lapCount = liveTimingState.LapCount; 47 | } 48 | } 49 | 50 | function getCarData(driverNumber) { 51 | try { 52 | carData[0].Cars[driverNumber].Channels; 53 | } catch (error) { 54 | return "error"; 55 | } 56 | return carData[0].Cars[driverNumber].Channels; 57 | } 58 | 59 | function getSpeedThreshold() { 60 | if ( 61 | sessionType === "Qualifying" || 62 | sessionType === "Practice" || 63 | trackStatus.Status === "4" || 64 | trackStatus.Status === "6" || 65 | trackStatus.Status === "7" 66 | ) 67 | return 10; 68 | if (sessionStatus === "Inactive" || sessionStatus === "Aborted") return 0; 69 | return 30; 70 | } 71 | 72 | function weirdCarBehaviour(driverCarData, racingNumber) { 73 | const driverTimingData = timingData[racingNumber]; 74 | 75 | const rpm = driverCarData[0]; 76 | 77 | const speed = driverCarData[2]; 78 | 79 | const gear = driverCarData[3]; 80 | 81 | const speedLimit = getSpeedThreshold(); 82 | 83 | return ( 84 | rpm === 0 || 85 | speed <= speedLimit || 86 | gear > 8 || 87 | gear === 88 | (sessionStatus === "Inactive" || 89 | sessionStatus === "Aborted" || 90 | (sessionType !== "Race" && driverTimingData.PitOut) 91 | ? "" 92 | : 0) 93 | ); 94 | } 95 | 96 | function overwriteCrashedStatus(racingNumber) { 97 | const driverTimingData = timingData[racingNumber]; 98 | 99 | if (driverTimingData.InPit === true) return true; 100 | if (driverTimingData.Retired === true) return true; 101 | if (driverTimingData.Stopped === true) return true; 102 | 103 | const lastSectorSegments = driverTimingData.Sectors.slice(-1)[0].Segments; 104 | 105 | const sessionInactive = 106 | sessionStatus === "Inactive" || sessionStatus === "Finished" || sessionStatus === "Finalised"; 107 | 108 | if (!lastSectorSegments && sessionInactive) return true; 109 | 110 | if (!lastSectorSegments) return false; 111 | 112 | // Detect if grid start during inactive (formation lap) during a 'Race' session 113 | // If the final to last mini sector has a value (is not 0). Check if the session is 'Inactive' and if the session type is 'Race' 114 | if (lastSectorSegments.slice(-2, -1)[0].Status !== 0 && sessionInactive && !driverTimingData.PitOut) { 115 | console.log(racingNumber + " is lining up for a race start"); 116 | return true; 117 | } 118 | 119 | // If the race is started and the last mini sector has a different value then 0 (has a value) 120 | if ( 121 | sessionType === "Race" && 122 | sessionStatus === "Started" && 123 | (lastSectorSegments.slice(-3)[0].Status !== 0 || driverTimingData.Sectors[0].Segments[1].Status === 0) && 124 | lapCount.CurrentLap === 1 125 | ) { 126 | console.log(racingNumber + " is doing a race start"); 127 | return true; 128 | } 129 | 130 | // Detect if practice pitstop 131 | // If the session is 'practice' and the second mini sector does have a value. 132 | if (sessionType === "Practice" && driverTimingData.PitOut) { 133 | console.log(racingNumber + " is doing a practice start"); 134 | return true; 135 | } 136 | 137 | // Detect if car is in parc ferme 138 | // If the car has stopped anywhere in the final sector and the 'race' has 'finished' 139 | if ( 140 | sessionType === "Race" && 141 | (sessionStatus === "Finished" || sessionStatus === "Finalised") && 142 | lastSectorSegments.some((segment) => segment.Status !== 0) 143 | ) { 144 | console.log(racingNumber + " is in parc ferme"); 145 | return true; 146 | } 147 | 148 | return false; 149 | } 150 | 151 | function driverHasCrashed(driverNumber) { 152 | const driverCarData = getCarData(driverNumber); 153 | 154 | if (!weirdCarBehaviour(driverCarData, driverNumber)) return false; 155 | 156 | if (overwriteCrashedStatus(driverNumber)) return false; 157 | 158 | return true; 159 | } 160 | 161 | async function run() { 162 | await getConfigurations(); 163 | while (true) { 164 | await apiRequests(); 165 | 166 | for (const i in driverList) { 167 | const driverInfo = driverList[i]; 168 | 169 | const driverNumber = driverInfo.RacingNumber; 170 | 171 | const name = driverInfo.FirstName 172 | ? `${driverInfo.FirstName} ${driverInfo.LastName.toUpperCase()}` 173 | : driverInfo.Tla; 174 | 175 | const color = driverInfo.TeamColour || "808080"; 176 | 177 | const driverCarData = getCarData(driverNumber); 178 | 179 | if (driverCarData !== "error") { 180 | const HTMLDisplayList = document.getElementById("list"); 181 | 182 | const driverElement = document.getElementById(driverNumber); 183 | 184 | const crashed = driverHasCrashed(driverNumber); 185 | 186 | if (crashed) { 187 | if (driverElement === null) { 188 | const newDriverElement = document.createElement("li"); 189 | newDriverElement.id = driverNumber; 190 | newDriverElement.style.color = "#" + color; 191 | newDriverElement.innerHTML = name; 192 | HTMLDisplayList.appendChild(newDriverElement); 193 | await sleep(10); 194 | newDriverElement.className = "show"; 195 | } 196 | 197 | console.log(name + " has crashed"); 198 | } else { 199 | if (driverElement !== null) { 200 | document.getElementById(driverNumber).className = ""; 201 | 202 | await sleep(400); 203 | 204 | driverElement.remove(); 205 | } 206 | } 207 | } 208 | } 209 | 210 | await sleep(250); 211 | 212 | const HTMLDisplayListLength = document.getElementById("list").childNodes.length; 213 | 214 | if (HTMLDisplayListLength > crashCount) triggerWarning(); 215 | crashCount = HTMLDisplayListLength; 216 | } 217 | } 218 | run(); 219 | 220 | async function triggerWarning() { 221 | console.log("trigger warning"); 222 | const title = document.querySelector("h1"); 223 | let loop = 0; 224 | while (loop <= 10) { 225 | await sleep(200); 226 | 227 | title.className = "warning"; 228 | 229 | await sleep(200); 230 | 231 | title.className = ""; 232 | 233 | loop++; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/tirestats/index.js: -------------------------------------------------------------------------------- 1 | const debug = false; 2 | 3 | const { ipcRenderer } = require("electron"); 4 | 5 | const f1mvApi = require("npm_f1mv_api"); 6 | 7 | const tireStats = { 8 | SOFT: { 9 | laps: 0, 10 | sets: 0, 11 | times: [], 12 | toptimes: [], 13 | }, 14 | MEDIUM: { 15 | laps: 0, 16 | sets: 0, 17 | times: [], 18 | toptimes: [], 19 | }, 20 | HARD: { 21 | laps: 0, 22 | sets: 0, 23 | times: [], 24 | toptimes: [], 25 | }, 26 | INTERMEDIATE: { 27 | laps: 0, 28 | sets: 0, 29 | times: [], 30 | toptimes: [], 31 | }, 32 | WET: { 33 | laps: 0, 34 | sets: 0, 35 | times: [], 36 | toptimes: [], 37 | }, 38 | TEST: { 39 | laps: 0, 40 | sets: 0, 41 | times: [], 42 | toptimes: [], 43 | }, 44 | }; 45 | 46 | const tireOrder = ["SOFT", "MEDIUM", "HARD", "INTERMEDIATE", "WET", "TEST"]; 47 | 48 | const topLimit = 3; 49 | 50 | const tireLimit = 3; 51 | 52 | const sleep = (milliseconds) => { 53 | return new Promise((resolve) => setTimeout(resolve, milliseconds)); 54 | }; 55 | 56 | document.addEventListener("keydown", (event) => { 57 | if (event.key == "Escape") document.getElementById("background").classList.toggle("transparent"); 58 | }); 59 | 60 | async function getConfigurations() { 61 | const configFile = (await ipcRenderer.invoke("get_store")).config; 62 | const networkConfig = configFile.network; 63 | const host = networkConfig.host; 64 | const port = (await f1mvApi.discoverF1MVInstances(host)).port; 65 | config = { 66 | host: host, 67 | port: port, 68 | }; 69 | } 70 | 71 | function parseLapTime(time) { 72 | const [minutes, seconds, milliseconds] = time 73 | .split(/[:.]/) 74 | .map((number) => parseInt(number.replace(/^0+/, "") || "0", 10)); 75 | 76 | if (milliseconds === undefined) return minutes + seconds / 1000; 77 | 78 | return minutes * 60 + seconds + milliseconds / 1000; 79 | } 80 | 81 | function getLapTime(time) { 82 | const minutes = Math.floor(time / 60); 83 | const seconds = time - minutes * 60; 84 | // const milliseconds = Math.floor((time - minutes * 60 - seconds) * 1000); 85 | 86 | const display0 = seconds < 10 ? "0" : ""; 87 | 88 | return `${minutes}:${display0}${seconds.toFixed(3)}`; 89 | } 90 | 91 | async function apiRequests() { 92 | const api = await f1mvApi.LiveTimingAPIGraphQL(config, ["DriverList", "TimingAppData"]); 93 | tireData = api.TimingAppData?.Lines; 94 | driverList = api.DriverList; 95 | } 96 | 97 | let pastTireData = {}; 98 | function getTireStats() { 99 | for (const compound in tireStats) { 100 | tireStats[compound].laps = 0; 101 | tireStats[compound].sets = 0; 102 | tireStats[compound].times = []; 103 | tireStats[compound].toptimes = []; 104 | } 105 | 106 | for (const driver in tireData) { 107 | const driverTireData = tireData[driver].Stints; 108 | 109 | console.log(driver); 110 | 111 | for (const compound of tireOrder) { 112 | let compoundTime = null; 113 | for (const stint of driverTireData) { 114 | const driverCompound = stint.Compound === "TEST_UNKNOWN" ? "TEST" : stint.Compound; 115 | 116 | if (driverCompound !== compound) continue; 117 | 118 | // Set laps and sets 119 | tireStats[compound].laps += stint.TotalLaps - stint.StartLaps; 120 | if (stint.New === "true") tireStats[compound].sets++; 121 | 122 | const lapTime = stint.LapTime ? parseLapTime(stint.LapTime) : null; 123 | 124 | if (lapTime && (lapTime < compoundTime || compoundTime === null)) compoundTime = lapTime; 125 | } 126 | 127 | if (!compoundTime) continue; 128 | 129 | tireStats[compound].times.push(compoundTime); 130 | 131 | const compountStats = tireStats[compound]; 132 | 133 | if (compountStats.toptimes.length < topLimit) { 134 | compountStats.toptimes.push({ 135 | driver: driver, 136 | time: compoundTime, 137 | }); 138 | } else { 139 | for (let count = 0; count < topLimit; count++) { 140 | if (compountStats.toptimes[count].time > compoundTime) { 141 | compountStats.toptimes[count] = { 142 | driver: driver, 143 | time: compoundTime, 144 | }; 145 | break; 146 | } 147 | } 148 | } 149 | 150 | tireStats[compound].toptimes.sort((a, b) => a.time - b.time); 151 | } 152 | } 153 | 154 | tireOrder.sort((a, b) => { 155 | const aTimeAverage = 156 | tireStats[a].times.reduce(function (aa, ab) { 157 | return aa + ab; 158 | }, 0) / tireStats[a].times.length; 159 | const bTimeAverage = 160 | tireStats[b].times.reduce(function (ba, bb) { 161 | return ba + bb; 162 | }, 0) / tireStats[b].times.length; 163 | if (aTimeAverage === bTimeAverage) return 0; 164 | if (aTimeAverage < bTimeAverage) return -1; 165 | if (isNaN(aTimeAverage)) return 1; 166 | if (isNaN(bTimeAverage)) return -1; 167 | return 1; 168 | }); 169 | 170 | console.log(tireStats); 171 | } 172 | 173 | function setTireStats() { 174 | const compoundElements = document.getElementsByClassName("tire"); 175 | 176 | for (let compoundIndex = 0; compoundIndex < tireLimit && compoundIndex < tireOrder.length; compoundIndex++) { 177 | const compound = tireOrder[compoundIndex]; 178 | 179 | const compoundStats = tireStats[compound]; 180 | 181 | const compoundElement = compoundElements[compoundIndex]; 182 | 183 | // Set compount name and image 184 | const tireImageElement = compoundElement.querySelector(".tire-image"); 185 | tireImageElement.children[0].src = `../icons/tires/${compound.toLowerCase()}_real.png`; 186 | tireImageElement.children[1].textContent = compound === "INTERMEDIATE" ? "INTERS" : compound; 187 | tireImageElement.children[1].className = compound.toLowerCase(); 188 | 189 | // Set top times 190 | const topTimesElements = compoundElement.querySelectorAll(".top-times .row"); 191 | for (const topTimeIndex in compoundStats.toptimes) { 192 | const topTime = compoundStats.toptimes[topTimeIndex]; 193 | const topTimeElement = topTimesElements[topTimeIndex]; 194 | 195 | // Set driver info 196 | const topDriverElement = topTimeElement.querySelector(".driver p"); 197 | 198 | topDriverElement.textContent = driverList[topTime.driver].LastName; 199 | topDriverElement.style.color = "#" + driverList[topTime.driver].TeamColour; 200 | 201 | // Set time info 202 | const topTimeElementTime = topTimeElement.querySelector(".time p"); 203 | topTimeElementTime.textContent = getLapTime(topTime.time); 204 | } 205 | 206 | // Set stats 207 | const statElements = compoundElement.querySelectorAll(".stats .row"); 208 | 209 | statElements[0].children[1].textContent = compoundStats.laps; 210 | statElements[1].children[1].textContent = compoundStats.sets; 211 | 212 | // Set delta 213 | 214 | if (compoundStats.times.length === 0 || tireStats[tireOrder[0]].times.length === 0) continue; 215 | 216 | const fastestAverage = 217 | tireStats[tireOrder[0]].times.reduce((a, b) => a + b) / tireStats[tireOrder[0]].times.length; 218 | 219 | if (compoundIndex === 0) continue; 220 | 221 | const deltaTime = compoundElement.querySelector(".delta .delta-time p"); 222 | 223 | const compoundTimeAverage = compoundStats.times.reduce((a, b) => a + b) / compoundStats.times.length; 224 | 225 | const delta = compoundTimeAverage - fastestAverage; 226 | 227 | deltaTime.textContent = `+${delta.toFixed(3)}`; 228 | } 229 | } 230 | 231 | async function run() { 232 | await getConfigurations(); 233 | await apiRequests(); 234 | getTireStats(); 235 | setTireStats(); 236 | setInterval(async () => { 237 | await apiRequests(); 238 | getTireStats(); 239 | setTireStats(); 240 | }, 5000); 241 | } 242 | 243 | run(); 244 | -------------------------------------------------------------------------------- /src/tirestats/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |UNKNOWN
23 |Top Times
27 |No Driver
31 |No Time
34 |No Driver
39 |No Time
42 |No Driver
47 |No Time
50 |Stats
56 |Laps Driven
59 |0
60 |Sets Used
63 |0
64 |Delta
69 |FASTEST
72 |UNKNOWN
79 |Top Times
83 |No Driver
87 |No Time
90 |No Driver
95 |No Time
98 |No Driver
103 |No Time
106 |Stats
112 |Laps Driven
115 |0
116 |Sets Used
119 |0
120 |Delta
125 |+0.000
128 |UNKNOWN
135 |Top Times
139 |No Driver
143 |No Time
146 |No Driver
151 |No Time
154 |No Driver
159 |No Time
162 |Stats
168 |Laps Driven
171 |0
172 |Sets Used
175 |0
176 |Delta
181 |+0.000
184 |
53 |
54 |
65 |
66 |
74 |
75 |
103 |
104 |
135 |
136 |
137 |
138 |
145 |
146 |
153 |
154 |
161 |
162 |
169 |
170 |
177 |
178 |
185 |
186 |
193 |
194 |
201 |
202 |
209 |
210 |
217 |
218 |
225 |
226 |
233 |
234 |
245 |
246 |
247 |
--------------------------------------------------------------------------------
/src/trackinfo/index.js:
--------------------------------------------------------------------------------
1 | const debug = false;
2 |
3 | const { ipcRenderer } = require("electron");
4 |
5 | const f1mvApi = require("npm_f1mv_api");
6 |
7 | // Set global variables
8 | const qualiPartLengths = ["00:18:00", "00:15:00", "00:12:00"];
9 | const extraTime = "01:00:00";
10 |
11 | async function getConfigurations() {
12 | const configFile = (await ipcRenderer.invoke("get_store")).config.trackinfo;
13 | const networkConfig = (await ipcRenderer.invoke("get_store")).config.network;
14 | const orientation = configFile.orientation;
15 | const styleType = orientation === "vertical" ? "w" : "h";
16 | const h2Elements = document.querySelectorAll("h2");
17 | for (const element of h2Elements) {
18 | element.classList.add(`h2${styleType}`);
19 | }
20 | const pElements = document.querySelectorAll("p");
21 | for (const element of pElements) {
22 | element.classList.add(`p${styleType}`);
23 | }
24 | pClass = `p${styleType}`;
25 | document.getElementById("wrapper").classList.add(`wrapper${styleType}`);
26 | document.getElementById("container").classList.add(`container${styleType}`);
27 |
28 | if (orientation === "horizontal") {
29 | document.getElementById("session-name-container").style.display = "none";
30 | }
31 |
32 | host = networkConfig.host;
33 | port = (await f1mvApi.discoverF1MVInstances(host)).port;
34 | }
35 |
36 | let sessionInfo;
37 |
38 | // Requesting the information needed from the api
39 | async function apiRequests() {
40 | const config = {
41 | host: host,
42 | port: port,
43 | };
44 | const data = await f1mvApi.LiveTimingAPIGraphQL(config, [
45 | "LapCount",
46 | "TrackStatus",
47 | "SessionStatus",
48 | "TimingData",
49 | "TimingStats",
50 | "ExtrapolatedClock",
51 | "SessionData",
52 | "RaceControlMessages",
53 | "SessionInfo",
54 | ]);
55 | lapCount = data.LapCount;
56 | trackStatus = data.TrackStatus;
57 | sessionStatus = data.SessionStatus.Status;
58 | timingData = data.TimingData;
59 | timingStats = data.TimingStats.Lines;
60 | extrapolatedClock = data.ExtrapolatedClock;
61 | sessionData = data.SessionData;
62 | raceControlMessages = data.RaceControlMessages.Messages;
63 | sessionInfo = data.SessionInfo;
64 | console.log(data);
65 | clockData = await f1mvApi.LiveTimingClockAPIGraphQL(config, [
66 | "paused",
67 | "systemTime",
68 | "trackTime",
69 | "liveTimingStartTime",
70 | ]);
71 | }
72 |
73 | function parseTime(time) {
74 | console.log(time);
75 | const [seconds, minutes, hours] = time
76 | .split(":")
77 | .reverse()
78 | .map((number) => parseInt(number));
79 |
80 | if (hours === undefined) return (minutes * 60 + seconds) * 1000;
81 |
82 | return (hours * 3600 + minutes * 60 + seconds) * 1000;
83 | }
84 |
85 | function parseLapTime(lapTime) {
86 | const [minutes, seconds, milliseconds] = lapTime
87 | .split(/[:.]/)
88 | .map((number) => parseInt(number.replace(/^0+/, "") || "0", 10));
89 |
90 | if (milliseconds === undefined) {
91 | return minutes + seconds / 1000;
92 | }
93 |
94 | return minutes * 60 + seconds + milliseconds / 1000;
95 | }
96 |
97 | function getTime(ms) {
98 | const date = new Date(ms);
99 |
100 | console.log(date);
101 |
102 | const hours = date.getUTCHours().toString().padStart(2, "0");
103 | const minutes = date.getUTCMinutes().toString().padStart(2, "0");
104 | const seconds = date.getUTCSeconds().toString().padStart(2, "0");
105 |
106 | if (parseInt(hours) === 0) {
107 | return `${minutes}:${seconds}`;
108 | }
109 |
110 | return `${hours}:${minutes}:${seconds}`;
111 | }
112 |
113 | // Set the status from the session
114 | function setSession() {
115 | let status = "ONSCHEDULE";
116 | let color = "green";
117 | switch (sessionStatus) {
118 | case "Started":
119 | status = "ONGOING";
120 | break;
121 | case "Aborted":
122 | status = "SUSPENDED";
123 | color = "red";
124 | break;
125 | case "Finished":
126 | case "Finalised":
127 | case "Ends":
128 | status = "FINISHED";
129 | color = "gray";
130 | break;
131 | case "Inactive":
132 | for (const message of raceControlMessages) {
133 | if (!message.Message.includes("DELAYED")) break;
134 | status = "DELAYED";
135 | color = "orange";
136 | }
137 | break;
138 | }
139 |
140 | const statusElement = document.getElementById("session");
141 | statusElement.textContent = status;
142 | statusElement.className = `${pClass} ${color}`;
143 |
144 | const sessionNameElement = document.getElementById("session-name");
145 | sessionNameElement.textContent = sessionInfo.Name.toUpperCase();
146 | }
147 |
148 | // Setting the timers for the current session
149 | function setTrackTime() {
150 | const now = new Date();
151 | const systemTime = clockData.systemTime;
152 | const trackTime = clockData.trackTime;
153 | const paused = clockData.paused;
154 |
155 | const offset = parseTime(sessionInfo.GmtOffset);
156 |
157 | console.log(offset);
158 |
159 | console.log(now.getTime());
160 | console.log(systemTime);
161 | console.log(trackTime);
162 |
163 | const localTime = parseInt(paused ? trackTime : now - (systemTime - trackTime)) + offset;
164 |
165 | const displayTime = getTime(localTime);
166 |
167 | const color = paused ? "gray" : "green";
168 |
169 | const trackTimeElement = document.getElementById("time");
170 | trackTimeElement.textContent = displayTime;
171 | trackTimeElement.className = `${pClass} ${color}`;
172 | }
173 |
174 | function setSessionTimer() {
175 | const now = new Date();
176 | const paused = clockData.paused;
177 | const extrapolatedClockStart = new Date(extrapolatedClock.Utc);
178 | const extrapolatedTime = extrapolatedClock.Remaining;
179 | const systemTime = clockData.systemTime;
180 | const trackTime = clockData.trackTime;
181 | const extrapolating = extrapolatedClock.Extrapolating;
182 |
183 | const sessionDuration = parseTime(extrapolatedTime) + 1000;
184 |
185 | console.log(sessionDuration);
186 |
187 | const timer = extrapolating
188 | ? paused
189 | ? getTime(sessionDuration - (trackTime - extrapolatedClockStart))
190 | : getTime(sessionDuration - (now - (systemTime - trackTime) - extrapolatedClockStart))
191 | : extrapolatedTime;
192 |
193 | const color = paused ? "gray" : extrapolating ? "green" : "gray";
194 |
195 | const sessionTimerElement = document.getElementById("session-timer");
196 | sessionTimerElement.textContent = timer;
197 | sessionTimerElement.className = `${pClass} ${color}`;
198 |
199 | return parseTime(timer);
200 | }
201 |
202 | function setExtraTimer() {
203 | const extraTimerElement = document.getElementById("extra-timer");
204 |
205 | if (sessionInfo.Type !== "Race" || !sessionData) {
206 | extraTimerElement.className = "hidden";
207 | return;
208 | }
209 |
210 | let color = "gray";
211 | let displayTime;
212 |
213 | const now = new Date();
214 | const paused = clockData.paused;
215 | const systemTime = clockData.systemTime;
216 | const trackTime = clockData.trackTime;
217 | const extrapolatedClockStart = new Date(extrapolatedClock.Utc);
218 |
219 | let extraTimeUsed = 0;
220 | let aborted = false;
221 | let startTime = 0;
222 | for (const status of sessionData.StatusSeries) {
223 | if (aborted && status.SessionStatus === "Started") {
224 | extraTimeUsed = new Date(status.Utc) - startTime;
225 | aborted = false;
226 | continue;
227 | }
228 |
229 | if (status.SessionStatus === "Aborted") {
230 | startTime = new Date(status.Utc);
231 | aborted = true;
232 | }
233 | }
234 |
235 | const extraTimeMs = parseTime(extraTime);
236 |
237 | const remaining = extraTimeMs - extraTimeUsed;
238 |
239 | if (sessionStatus === "Aborted" && !extrapolatedClock.Extrapolating) {
240 | displayTime = paused
241 | ? getTime(remaining - (trackTime - extrapolatedClockStart))
242 | : getTime(remaining - (now - (systemTime - trackTime) - extrapolatedClockStart));
243 | color = "green";
244 | } else {
245 | displayTime = remaining > 0 ? getTime(remaining) : getTime(0);
246 | }
247 |
248 | extraTimerElement.textContent = displayTime;
249 | extraTimerElement.className = `${pClass} ${color}`;
250 | }
251 |
252 | function setProgress(timer) {
253 | const sessionLength = new Date(sessionInfo.EndDate) - new Date(sessionInfo.StartDate);
254 |
255 | const progressElement = document.getElementById("progress");
256 |
257 | let totalTimers = sessionLength;
258 |
259 | if (sessionInfo.Type === "Race") {
260 | document.getElementById("quali-part").className = "hidden";
261 |
262 | let fastestLap = 0;
263 | for (const driverNumber in timingStats) {
264 | const driverTimingStats = timingStats[driverNumber];
265 |
266 | if (driverTimingStats.PersonalBestLapTime.Position === 1) {
267 | fastestLap = parseLapTime(driverTimingStats.PersonalBestLapTime.Value);
268 | break;
269 | }
270 | }
271 |
272 | console.log(timer);
273 |
274 | const finished = sessionStatus === "Finished" || sessionStatus === "Finalised" || sessionStatus === "Ends";
275 |
276 | const maxLaps =
277 | fastestLap === 0
278 | ? lapCount.TotalLaps
279 | : finished
280 | ? lapCount.CurrentLap
281 | : Math.ceil(timer / (fastestLap * 1000) + lapCount.CurrentLap + 1);
282 |
283 | const maxLapPercentage = Math.floor((maxLaps / lapCount.TotalLaps) * 100);
284 | const color = finished
285 | ? "gray"
286 | : maxLaps < lapCount.TotalLaps
287 | ? maxLapPercentage <= 75
288 | ? "red"
289 | : "orange"
290 | : "green";
291 |
292 | console.log(lapCount.CurrentLap / lapCount.TotalLaps);
293 |
294 | const lapPercentage = Math.floor((lapCount.CurrentLap / lapCount.TotalLaps) * 100);
295 |
296 | progressElement.textContent =
297 | maxLapPercentage === 100 && finished
298 | ? "CONCLUDED"
299 | : `${lapPercentage === 100 ? "99" : lapPercentage}% - ${
300 | maxLapPercentage > 100 ? "100" : maxLapPercentage
301 | }%`;
302 | progressElement.className = `${pClass} ${color}`;
303 |
304 | const displayLapCount = `Lap: ${lapCount.CurrentLap}/${
305 | maxLaps > lapCount.TotalLaps ? lapCount.TotalLaps : maxLaps
306 | }`;
307 |
308 | const lapCountElement = document.getElementById("lapcount");
309 |
310 | lapCountElement.textContent = displayLapCount;
311 | lapCountElement.className = `${pClass} ${color}`;
312 | } else {
313 | document.getElementById("lapcount").className = "hidden";
314 |
315 | if (sessionInfo.Type === "Qualifying") {
316 | const qualiPartElement = document.getElementById("quali-part");
317 |
318 | const qualiPart = timingData.SessionPart;
319 | totalTimers = parseTime(qualiPartLengths[qualiPart - 1]);
320 |
321 | qualiPartElement.textContent = `Q${qualiPart} - Q${qualiPartLengths.length}`;
322 | qualiPartElement.classList = `${pClass} ${timer === 0 && qualiPart === 3 ? "gray" : "green"}`;
323 | } else {
324 | document.getElementById("quali-part").className = "hidden";
325 | }
326 |
327 | const timerProgress = totalTimers - timer;
328 |
329 | const progress = Math.floor((timerProgress / totalTimers) * 100);
330 |
331 | const max = 100;
332 |
333 | const sessionEnded = timer === 0;
334 |
335 | const displayProgress = sessionEnded ? "CONCLUDED" : `${progress}% - ${max}%`;
336 |
337 | progressElement.textContent = displayProgress;
338 | progressElement.className = `${pClass} ${sessionEnded ? "gray" : "green"}`;
339 |
340 | console.log(progress);
341 |
342 | console.log(timer);
343 | console.log(totalTimers);
344 | }
345 | }
346 |
347 | function setPitlane(message) {
348 | if (
349 | !(
350 | message.SubCategory === "PitEntry" ||
351 | message.SubCategory === "PitExit" ||
352 | message.Flag === "RED" ||
353 | sessionStatus === "Aborted"
354 | )
355 | )
356 | return;
357 |
358 | const pitExitElement = document.getElementById(`pit-exit`);
359 |
360 | if (message.Flag === "RED") {
361 | pitExitElement.className = `${pClass} red`;
362 | return;
363 | }
364 |
365 | const type = message.SubCategory === "PitEntry" ? "entry" : "exit";
366 |
367 | let color = "red";
368 | switch (message.Flag) {
369 | case "OPEN":
370 | color = "green";
371 | break;
372 | case "CLOSED":
373 | color = "red";
374 | break;
375 | }
376 |
377 | const pitLaneElement = document.getElementById(`pit-${type}`);
378 | pitLaneElement.textContent = type.toUpperCase();
379 | pitLaneElement.className = `${pClass} ${color}`;
380 | }
381 |
382 | // Setting the grip status to the information screen
383 | function setGrip(message) {
384 | switch (message.SubCategory) {
385 | case "LowGripConditions":
386 | grip = "LOW";
387 | color = "orange";
388 | break;
389 | case "NormalGripConditions":
390 | grip = "NORMAL";
391 | color = "green";
392 | break;
393 | default:
394 | return;
395 | }
396 |
397 | const gripElement = document.getElementById("grip");
398 | gripElement.textContent = grip;
399 | gripElement.className = `${pClass} ${color}`;
400 | }
401 |
402 | function setHeadPadding(message) {
403 | if (!(message.Category === "Other" && message.Message.includes("HEAD PADDING MATERIAL"))) return;
404 |
405 | let padding = "UNKNOWN";
406 | let color = "white";
407 | if (message.Message.includes("BLUE")) {
408 | padding = "BLUE";
409 | color = "blue";
410 | } else if (message.Message.includes("LIGHT BLUE")) {
411 | padding = "LIGHT BLUE";
412 | color = "light-blue";
413 | } else if (message.Message.includes("PINK")) {
414 | padding = "PINK";
415 | color = "pink";
416 | }
417 | const headPaddingElement = document.getElementById("head-padding");
418 |
419 | headPaddingElement.textContent = padding;
420 | headPaddingElement.className = `${pClass} ${color}`;
421 | }
422 |
423 | function setDrs(message) {
424 | const category = message.Category;
425 |
426 | if (category !== "Drs") return;
427 |
428 | let status = "DISABLED";
429 | let color = "red";
430 | switch (message.Status) {
431 | case "DISABLED":
432 | status = message.Status;
433 | color = "red";
434 | break;
435 | case "ENABLED":
436 | status = message.Status;
437 | color = "green";
438 | break;
439 | }
440 |
441 | const drsElement = document.getElementById("drs");
442 | drsElement.textContent = status;
443 | drsElement.className = `${pClass} ${color}`;
444 | }
445 |
446 | function setManTires(message) {
447 | if (!message.Message.includes("TYRES" || "TIRES")) return;
448 |
449 | let tires = "NONE";
450 | let color = "white";
451 |
452 | if (message.Message.includes("USE OF WET WEATHER")) {
453 | tires = "INTERMEDIATES";
454 | backgroundColor = "green";
455 | } else if (message.Message.includes("EXTREME TIRES" || "EXTREME TYRES")) {
456 | tires = "FULL WETS";
457 | backgroundColor = "blue";
458 | }
459 |
460 | const manTiresElement = document.getElementById("tires");
461 | manTiresElement.textContent = tires;
462 | manTiresElement.className = `${pClass} ${color}`;
463 | }
464 |
465 | const pastMessages = [];
466 | function forRaceControlMessages() {
467 | for (const message of raceControlMessages) {
468 | if (pastMessages.includes(JSON.stringify(message))) continue;
469 | pastMessages.push(JSON.stringify(message));
470 | console.log(message);
471 | setGrip(message);
472 | setHeadPadding(message);
473 | setDrs(message);
474 | setPitlane(message);
475 | }
476 | }
477 |
478 | // Running all the functions
479 | async function run() {
480 | await getConfigurations();
481 | await apiRequests();
482 | setSession();
483 | setTrackTime();
484 | const timer = setSessionTimer();
485 | setExtraTimer();
486 | setProgress(timer);
487 | forRaceControlMessages();
488 | }
489 |
490 | // Running the whole screen
491 | run();
492 | setInterval(async () => {
493 | await run();
494 | }, 500);
495 |
--------------------------------------------------------------------------------