Guest User

Script for Digital Speedometer

a guest
Apr 4th, 2026
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.         <script type="module">
  2.             import { HudiyClient } from "./common/hudiy_client.js";
  3.  
  4.             const gaugeCanvas = document.getElementById("gaugeCanvas");
  5.             const engineLight = document.getElementById("g246");
  6.             const tempLight = document.getElementById("g243");
  7.             const gaugeCtx = gaugeCanvas.getContext("2d");
  8.  
  9.             const minSpeed = 0;
  10.             const maxSpeed = 120;
  11.             const maxRPM = 6;
  12.             let speed = 0;
  13.             let targetSpeed = 0;
  14.             let targetRPM = 0;
  15.             let engineTemp = 0;
  16.             let rpm = 0;
  17.             let animating = false;
  18.             let batVoltage = 0.0 + "V";
  19.             let checkEngineVisible = false;
  20.             let engineTempVisible = false;
  21.             let oilLife = 0 + "%";
  22.  
  23.             function estOilLife(lastChanged) {
  24.                 const currentDate = moment();
  25.                 const futureDate = moment(lastChanged);
  26.  
  27.                 const daysDiff = futureDate.diff(currentDate, "days");
  28.                 oilLife = Math.round(365 / daysDiff) + "%";
  29.  
  30.                 return oilLife;
  31.             }
  32.  
  33.             function drawNumSp(xoff, yoff, speed) {
  34.                 gaugeCtx.font = "118px 'Arial Black'";
  35.                 gaugeCtx.fillStyle = "white";
  36.                 gaugeCtx.fillText(speed.toString(), xoff, yoff);
  37.             }
  38.  
  39.             function drawNumTac(xoff, yoff, speed) {
  40.                 gaugeCtx.font = "118px 'Arial Black'";
  41.                 gaugeCtx.fillStyle = "white";
  42.                 gaugeCtx.fillText(speed.toString(), xoff, yoff);
  43.             }
  44.  
  45.             function drawNumBat(xoff, yoff, speed) {
  46.                 gaugeCtx.font = "20px 'Arial Black'";
  47.                 gaugeCtx.fillStyle = "white";
  48.                 gaugeCtx.fillText(speed.toString(), xoff, yoff);
  49.             }
  50.  
  51.             function drawNumOil(xoff, yoff, speed) {
  52.                 gaugeCtx.font = "20px 'Arial Black'";
  53.                 gaugeCtx.fillStyle = "white";
  54.                 gaugeCtx.fillText(speed.toString(), xoff, yoff);
  55.             }
  56.  
  57.             function drawSpeed(xoff, yoff, xmul, ymul) {
  58.                 gaugeCtx.beginPath();
  59.                 gaugeCtx.moveTo(171 * xmul + xoff, 456 * ymul + yoff);
  60.                 gaugeCtx.bezierCurveTo(
  61.                     151 * xmul + xoff,
  62.                     456 * ymul + yoff,
  63.                     139 * xmul + xoff,
  64.                     454 * ymul + yoff,
  65.                     119 * xmul + xoff,
  66.                     454 * ymul + yoff,
  67.                 );
  68.                 gaugeCtx.bezierCurveTo(
  69.                     94 * xmul + xoff,
  70.                     451 * ymul + yoff,
  71.                     58 * xmul + xoff,
  72.                     402 * ymul + yoff,
  73.                     43 * xmul + xoff,
  74.                     377 * ymul + yoff,
  75.                 );
  76.                 gaugeCtx.bezierCurveTo(
  77.                     33.51800353929965 * xmul + xoff,
  78.                     359.3905780015565 * ymul + yoff,
  79.                     8 * xmul + xoff,
  80.                     312 * ymul + yoff,
  81.                     6 * xmul + xoff,
  82.                     281 * ymul + yoff,
  83.                 );
  84.                 gaugeCtx.bezierCurveTo(
  85.                     4.712354440040691 * xmul + xoff,
  86.                     261.0414938206308 * ymul + yoff,
  87.                     39 * xmul + xoff,
  88.                     214 * ymul + yoff,
  89.                     91 * xmul + xoff,
  90.                     176 * ymul + yoff,
  91.                 );
  92.                 gaugeCtx.bezierCurveTo(
  93.                     106.7370683471181 * xmul + xoff,
  94.                     163.65720129637796 * ymul + yoff,
  95.                     138 * xmul + xoff,
  96.                     140 * ymul + yoff,
  97.                     182 * xmul + xoff,
  98.                     132 * ymul + yoff,
  99.                 );
  100.                 gaugeCtx.bezierCurveTo(
  101.                     201 * xmul + xoff,
  102.                     128 * ymul + yoff,
  103.                     280 * xmul + xoff,
  104.                     116 * ymul + yoff,
  105.                     368 * xmul + xoff,
  106.                     111 * ymul + yoff,
  107.                 );
  108.                 gaugeCtx.lineTo(368 * xmul + xoff, 135 * ymul + yoff);
  109.                 gaugeCtx.lineTo(339 * xmul + xoff, 138 * ymul + yoff);
  110.                 gaugeCtx.bezierCurveTo(
  111.                     359 * xmul + xoff,
  112.                     138 * ymul + yoff,
  113.                     251 * xmul + xoff,
  114.                     147 * ymul + yoff,
  115.                     191 * xmul + xoff,
  116.                     158 * ymul + yoff,
  117.                 );
  118.                 gaugeCtx.bezierCurveTo(
  119.                     171.327868852459 * xmul + xoff,
  120.                     161.60655737704917 * ymul + yoff,
  121.                     131 * xmul + xoff,
  122.                     185 * ymul + yoff,
  123.                     109 * xmul + xoff,
  124.                     201 * ymul + yoff,
  125.                 );
  126.                 gaugeCtx.bezierCurveTo(
  127.                     97 * xmul + xoff,
  128.                     212 * ymul + yoff,
  129.                     49 * xmul + xoff,
  130.                     249 * ymul + yoff,
  131.                     46 * xmul + xoff,
  132.                     279 * ymul + yoff,
  133.                 );
  134.                 gaugeCtx.bezierCurveTo(
  135.                     44.009925619580024 * xmul + xoff,
  136.                     298.9007438041998 * ymul + yoff,
  137.                     67 * xmul + xoff,
  138.                     344 * ymul + yoff,
  139.                     74 * xmul + xoff,
  140.                     358 * ymul + yoff,
  141.                 );
  142.                 gaugeCtx.bezierCurveTo(
  143.                     86 * xmul + xoff,
  144.                     375 * ymul + yoff,
  145.                     102 * xmul + xoff,
  146.                     407 * ymul + yoff,
  147.                     122 * xmul + xoff,
  148.                     419 * ymul + yoff,
  149.                 );
  150.                 gaugeCtx.bezierCurveTo(
  151.                     139.1498585142509 * xmul + xoff,
  152.                     429.28991510855053 * ymul + yoff,
  153.                     195 * xmul + xoff,
  154.                     430 * ymul + yoff,
  155.                     175 * xmul + xoff,
  156.                     430 * ymul + yoff,
  157.                 );
  158.                 gaugeCtx.closePath();
  159.             }
  160.  
  161.             function drawBattery(xoff, yoff, xmul, ymul) {
  162.                 gaugeCtx.beginPath();
  163.                 gaugeCtx.lineTo(533 * xmul + xoff, 385 * ymul + yoff);
  164.                 gaugeCtx.lineTo(533 * xmul + xoff, 375 * ymul + yoff);
  165.                 gaugeCtx.lineTo(395 * xmul + xoff, 374 * ymul + yoff);
  166.                 gaugeCtx.lineTo(395 * xmul + xoff, 386 * ymul + yoff);
  167.                 gaugeCtx.closePath();
  168.             }
  169.             function drawBat(xoff, yoff, xmul, ymul, fillPercent) {
  170.                 gaugeCtx.save();
  171.                 drawBattery(xoff, yoff, xmul, ymul);
  172.                 gaugeCtx.clip();
  173.                 gaugeCtx.filter = "blur(7px)";
  174.                 const leftX = 397 * xmul + xoff;
  175.                 const rightX = 533 * xmul + xoff;
  176.                 const fill = (leftX - rightX) * fillPercent;
  177.                 const fillX = leftX - fill;
  178.                 const grad = gaugeCtx.createLinearGradient(fillX, 0, leftX, 0);
  179.                 gaugeCtx.fillStyle = grad;
  180.                 grad.addColorStop(0, "rgba(240, 240, 255, 0.0)");
  181.                 grad.addColorStop(0, "rgba(255, 73, 0, 1)");
  182.                 grad.addColorStop(1, "rgba(255, 73, 0, 0.7)");
  183.                 gaugeCtx.fillRect(leftX, 0, fillX, gaugeCanvas.height);
  184.                 gaugeCtx.restore();
  185.             }
  186.             // Draw the gauge with liquid fill
  187.             function drawTac(xoff, yoff, xmul, ymul, fillPercent) {
  188.                 gaugeCtx.save();
  189.  
  190.                 // Define the shape
  191.  
  192.                 drawSpeed(xoff, yoff, xmul * -1, ymul);
  193.  
  194.                 // Clip to this shape
  195.                 gaugeCtx.clip();
  196.                 gaugeCtx.filter = "blur(10px)";
  197.  
  198.                 // Now draw the liquid fill
  199.                 const bottomY = 453 * ymul + yoff;
  200.                 const topY = 113 * ymul + yoff;
  201.                 const fillHeight = (bottomY - topY) * fillPercent;
  202.                 const fillY = bottomY - fillHeight;
  203.  
  204.                 const grad = gaugeCtx.createLinearGradient(
  205.                     0,
  206.                     fillY,
  207.                     0,
  208.                     bottomY,
  209.                 );
  210.                 grad.addColorStop(0, "rgba(240, 240, 255, 0.5)");
  211.                 grad.addColorStop(0.3, "#ee004d");
  212.                 grad.addColorStop(1, "#ee004d");
  213.  
  214.                 gaugeCtx.fillStyle = grad;
  215.                 gaugeCtx.fillRect(
  216.                     0,
  217.                     fillY,
  218.                     gaugeCanvas.width,
  219.                     gaugeCanvas.height,
  220.                 );
  221.                 gaugeCtx.restore();
  222.             }
  223.  
  224.             function drawGauge(xoff, yoff, xmul, ymul, fillPercent) {
  225.                 gaugeCtx.clearRect(0, 0, gaugeCanvas.width, gaugeCanvas.height);
  226.  
  227.                 gaugeCtx.save();
  228.  
  229.                 // Define the shape
  230.                 drawSpeed(xoff, yoff, xmul, ymul);
  231.  
  232.                 // Clip to this shape
  233.                 gaugeCtx.clip();
  234.                 gaugeCtx.filter = "blur(10px)";
  235.  
  236.                 // Now draw the liquid fill
  237.                 const bottomY = 453 * ymul + yoff;
  238.                 const topY = 113 * ymul + yoff;
  239.                 const fillHeight = (bottomY - topY) * fillPercent;
  240.                 const fillY = bottomY - fillHeight;
  241.  
  242.                 const grad = gaugeCtx.createLinearGradient(
  243.                     0,
  244.                     fillY,
  245.                     0,
  246.                     bottomY,
  247.                 );
  248.                 grad.addColorStop(0, "rgba(240, 240, 255, 0.5)");
  249.                 grad.addColorStop(0.3, "#ee004d");
  250.                 grad.addColorStop(1, "#ee004d");
  251.  
  252.                 gaugeCtx.fillStyle = grad;
  253.                 gaugeCtx.fillRect(
  254.                     0,
  255.                     fillY,
  256.                     gaugeCanvas.width,
  257.                     gaugeCanvas.height,
  258.                 );
  259.                 gaugeCtx.restore();
  260.             }
  261.  
  262.             function animateGauge() {
  263.                 if (Math.abs(targetSpeed - speed) >= 1) {
  264.                     // if difference between target and current is too great
  265.                     speed += (targetSpeed - speed) * 0.15; // increment the speed to keep up.
  266.                 } else {
  267.                     speed = targetSpeed; // else, the speed should just equal whatever the current speed is.
  268.                 }
  269.                 //prevents jittering
  270.                 if (Math.abs(targetRPM - rpm) >= 1) {
  271.                     // if the difference between rpm and target rpm is more than one (meaning it is moving)
  272.                     rpm += (targetRPM - rpm) * 0.15; // increment the rpm
  273.                 } else {
  274.                     rpm = targetRPM; // else, the rpm should just be whatever the target rpm is
  275.                 }
  276.  
  277.                 gaugeCtx.clearRect(0, 0, gaugeCanvas.width, gaugeCanvas.height); // clear previous frame
  278.                 const fillPercent = speed / maxSpeed;
  279.                 const tacFillPercent = rpm / maxRPM;
  280.  
  281.                 drawGauge(0, -165, 2.3, 2.3, fillPercent); // draw speedometor
  282.                 drawNumSp(330, 538, Math.round(speed)); // draw numerical representation of speed
  283.                 drawTac(1725, -165, 2.3, 2.3, tacFillPercent); // draw tachometor
  284.                 drawNumTac(1340, 538, Math.round(rpm)); // draw numeriocal representation of rpm
  285.                 drawNumBat(948, 848, batVoltage);
  286.                 drawNumOil(730, 848, estOilLife("2026-03-05"));
  287.                 requestAnimationFrame(animateGauge);
  288.             }
  289.  
  290.             function startAnimation() {
  291.                 if (!animating) {
  292.                     animating = true;
  293.                     requestAnimationFrame(animateGauge);
  294.                 }
  295.             }
  296.  
  297.             function resizeAndRenderStatic() {
  298.                 const rect = gaugeCanvas.getBoundingClientRect();
  299.                 gaugeCanvas.width = rect.width;
  300.                 gaugeCanvas.height = rect.height;
  301.  
  302.                 const fillPercent = speed / maxSpeed;
  303.                 let numSpd = "0";
  304.                 let numTac = "0";
  305.                 let numOil = "0%";
  306.                 //let numBat = "12.6v";
  307.                 drawGauge(0, -165, 2.3, 2.3, 0);
  308.                 drawTac(1725, -165, 2.3, 2.3, 0);
  309.                 drawNumSp(330, 538, numSpd);
  310.                 drawNumTac(1340, 538, numTac);
  311.                 drawNumBat(948, 848, "0V");
  312.                 drawNumOil(730, 848, estOilLife("2026-03-05"));
  313.             }
  314.  
  315.             const hudiy = { api: {} };
  316.             const root = await protobuf.load("common/Api.proto");
  317.  
  318.             hudiy.api.protoRoot = root;
  319.             hudiy.api.HelloRequest = root.lookupType(
  320.                 "hudiy.app.api.HelloRequest",
  321.             );
  322.             hudiy.api.HelloResponse = root.lookupType(
  323.                 "hudiy.app.api.HelloResponse",
  324.             );
  325.             hudiy.api.HelloResponseResult = root.lookup(
  326.                 "hudiy.app.api.HelloResponse.HelloResponseResult",
  327.             ).values;
  328.             hudiy.api.QueryObdDeviceRequest = root.lookupType(
  329.                 "hudiy.app.api.QueryObdDeviceRequest",
  330.             );
  331.             hudiy.api.QueryObdDeviceResponse = root.lookupType(
  332.                 "hudiy.app.api.QueryObdDeviceResponse",
  333.             );
  334.             hudiy.api.MessageType = root.lookup(
  335.                 "hudiy.app.api.MessageType",
  336.             ).values;
  337.             hudiy.api.client = new HudiyClient("ws://localhost:44406");
  338.  
  339.             function sendQueryObdRequest(OBDCode, rNum) {
  340.                 const msg = hudiy.api.QueryObdDeviceRequest.create({
  341.                     commands: OBDCode,
  342.                     requestCode: rNum,
  343.                 });
  344.                 const payload =
  345.                     hudiy.api.QueryObdDeviceRequest.encode(msg).finish();
  346.                 hudiy.api.client.sendMessage(
  347.                     hudiy.api.MessageType.MESSAGE_QUERY_OBD_DEVICE_REQUEST,
  348.                     0,
  349.                     payload,
  350.                 );
  351.             }
  352.  
  353.             hudiy.api.client.onOpen = () => {
  354.                 const hello = hudiy.api.HelloRequest.create({
  355.                     name: "DigiDash",
  356.                     apiVersion: { major: 1, minor: 0 },
  357.                 });
  358.                 const payload = hudiy.api.HelloRequest.encode(hello).finish();
  359.                 hudiy.api.client.sendMessage(
  360.                     hudiy.api.MessageType.MESSAGE_HELLO_REQUEST,
  361.                     0,
  362.                     payload,
  363.                 );
  364.             };
  365.  
  366.             hudiy.api.client.onMessage = ({ id, payload }) => {
  367.                 if (id === hudiy.api.MessageType.MESSAGE_HELLO_RESPONSE) {
  368.                     const msg = hudiy.api.HelloResponse.decode(payload);
  369.                     if (
  370.                         msg.result ===
  371.                         hudiy.api.HelloResponseResult.HELLO_RESPONSE_RESULT_OK
  372.                     ) {
  373.                         sendQueryObdRequest(["010D"], 0); // query speed
  374.                         sendQueryObdRequest(["010C"], 1); // query RPM
  375.                         sendQueryObdRequest(["0101"], 2); // check system integrity test (check engine light)
  376.                         sendQueryObdRequest(["0105"], 3); // check engine coolant temp
  377.                         sendQueryObdRequest(["0142"], 4); // check battery voltage
  378.                     }
  379.                 } else if (
  380.                     id ===
  381.                     hudiy.api.MessageType.MESSAGE_QUERY_OBD_DEVICE_RESPONSE
  382.                 ) {
  383.                     const msg =
  384.                         hudiy.api.QueryObdDeviceResponse.decode(payload);
  385.                     if (msg.requestCode === 0 && msg.result) {
  386.                         // get speed
  387.                         const bytes = msg.data[0]
  388.                             ?.match(/.{1,2}/g)
  389.                             ?.map((h) => parseInt(h, 16));
  390.                         targetSpeed = bytes ? bytes[2] / 1.609 : 0; // note that the output is divided by 1.609 to convert from kmh to mph
  391.                         startAnimation();
  392.                         setTimeout(() => sendQueryObdRequest(["010D"], 0), 150);
  393.                     } else if (msg.requestCode === 1 && msg.result) {
  394.                         // get RPM
  395.                         const bytes = msg.data[0]
  396.                             ?.match(/.{1,2}/g)
  397.                             ?.map((h) => parseInt(h, 16));
  398.                         targetRPM = bytes
  399.                             ? (256 * bytes[2] + bytes[3]) / 4000
  400.                             : 0;
  401.                         startAnimation();
  402.                         setTimeout(() => sendQueryObdRequest(["010C"], 1), 150);
  403.                         // below is used to engage the warning lights (check engine and engine temp warning)
  404.                     } else if (
  405.                         msg.requestCode === 2 &&
  406.                         msg.result &&
  407.                         checkEngineVisible == false
  408.                     ) {
  409.                         checkEngineVisible = true;
  410.                         engineLight.style.opacity = "1";
  411.                         setTimeout(
  412.                             () => sendQueryObdRequest(["0101"], 2),
  413.                             60000,
  414.                         );
  415.                     } else if (
  416.                         msg.requestCode === 2 &&
  417.                         !msg.result &&
  418.                         checkEngineVisible == true
  419.                     ) {
  420.                         checkEngineVisible = false;
  421.                         engineLight.style.opacity = "0.2";
  422.                         setTimeout(
  423.                             () => sendQueryObdRequest(["0101"], 2),
  424.                             60000,
  425.                         );
  426.                     } else if (msg.requestCode === 3 && msg.result) {
  427.                         // get engine temp
  428.                         const bytes = msg.data[0]
  429.                             ?.match(/.{1,2}/g)
  430.                             ?.map((h) => parseInt(h, 16));
  431.                         engineTemp = bytes ? bytes[2] - 40 : 0;
  432.  
  433.                         // check whether engine is overheating
  434.                         if (engineTemp > 105 && engineTempVisible == false) {
  435.                             engineTempVisible = true;
  436.                             tempLight.style.opacity = "1";
  437.                         } else if (
  438.                             engineTemp < 105 &&
  439.                             engineTempVisible == true
  440.                         ) {
  441.                             engineTempVisible = false;
  442.                             tempLight.style.opacity = "0.2";
  443.                         }
  444.                         setTimeout(
  445.                             () => sendQueryObdRequest(["0105"], 3),
  446.                             60000,
  447.                         );
  448.                     } else if (msg.requestCode === 4 && msg.result) {
  449.                         // get battery voltage
  450.                         const bytes = msg.data[0]
  451.                             ?.match(/.{1,2}/g)
  452.                             ?.map((h) => parseInt(h, 16));
  453.                         batVoltage = bytes
  454.                             ? ((256 * bytes[2] + bytes[3]) / 1000).toFixed(1) +
  455.                               "V"
  456.                             : 0;
  457.                         startAnimation();
  458.                         setTimeout(() => sendQueryObdRequest(["0142"], 4), 150);
  459.                     }
  460.                 } else if (id === hudiy.api.MessageType.MESSAGE_PING) {
  461.                     hudiy.api.client.sendMessage(
  462.                         hudiy.api.MessageType.MESSAGE_PONG,
  463.                         0,
  464.                         new Uint8Array(0),
  465.                     );
  466.                 }
  467.             };
  468.  
  469.             hudiy.api.client.connect();
  470.  
  471.             window.addEventListener("resize", resizeAndRenderStatic);
  472.             resizeAndRenderStatic();
  473.         </script>
  474.  
Add Comment
Please, Sign In to add comment