Code for Frontend and Backend

Frontend

HTML

<!DOCTYPE html>
<html>
 
<head>
  <meta charset="utf-8">
  <title>HeartbeatOS</title>
  <link rel="stylesheet" href="style.css">
  <link rel="icon" type="image/x-icon" href="/assets/favicon.png">
  <meta name="viewport" content="initial-scale=1" />
 
  <!-- jQuery needed for the Audioplayer -->
  <script src="./libs/jquery.js"></script>
</head>
 
<body>
  <nav class="navigation">
    <a class="logo" href="#">
      <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
        stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
        class="feather feather-activity">
        <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
      </svg>
      <p>HeartbeatOS</p>
    </a>
    <button class="menu-button">
      Documentation
    </button>
  </nav>
  <main class="main">
    <div class="content">
      <div class="container">
        <p>Sensor</p>
        <div class="buttons">
          <button class="firstbtn" id="start">Activate</button>
          <button class="secondbtn activeState" id="stop">Deactivate</button>
        </div>
      </div>
      <div class="container">
        <p>Revive Heart</p>
        <a class="button no-pointer" id="revive">
          <div id="circle" class="circle"></div><span id="reviveText">Inactive</span>
        </a>
      </div>
      <div class="container big">
        <p>Graph</p>
        <div class="graph">
          <div class="graph-overlay"></div>
          <canvas id="chart"></canvas>
        </div>
      </div>
      <div class="container big tworows">
        <div class="linear-gradient-overlay"></div>
        <p>Latest Logs</p>
        <div class="scroll-container">
          <div>
            <div class="logs" id="logs">
            </div>
          </div>
        </div>
      </div>
      <div class="container big">
        <p>Heart Rate</p>
        <div class="box">
          <div class="heart-wrapper">
            <svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path
                d="M52.1003 11.525C50.8234 10.2475 49.3073 9.23411 47.6387 8.5427C45.97 7.8513 44.1815 7.49543 42.3753 7.49543C40.5691 7.49543 38.7806 7.8513 37.1119 8.5427C35.4433 9.23411 33.9272 10.2475 32.6503 11.525L30.0003 14.175L27.3503 11.525C24.7711 8.94576 21.2729 7.49677 17.6253 7.49677C13.9777 7.49677 10.4795 8.94576 7.9003 11.525C5.32107 14.1042 3.87207 17.6024 3.87207 21.25C3.87207 24.8976 5.32107 28.3958 7.9003 30.975L10.5503 33.625L30.0003 53.075L49.4503 33.625L52.1003 30.975C53.3778 29.6981 54.3912 28.182 55.0826 26.5134C55.774 24.8447 56.1299 23.0562 56.1299 21.25C56.1299 19.4438 55.774 17.6553 55.0826 15.9866C54.3912 14.318 53.3778 12.8019 52.1003 11.525Z"
                fill="#FF3B30" stroke="#FF3B30" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
            </svg>
            <svg class="heart-animation" width="120" height="120" viewBox="0 0 60 60" fill="none"
              xmlns="http://www.w3.org/2000/svg">
              <path
                d="M52.1003 11.525C50.8234 10.2475 49.3073 9.23411 47.6387 8.5427C45.97 7.8513 44.1815 7.49543 42.3753 7.49543C40.5691 7.49543 38.7806 7.8513 37.1119 8.5427C35.4433 9.23411 33.9272 10.2475 32.6503 11.525L30.0003 14.175L27.3503 11.525C24.7711 8.94576 21.2729 7.49677 17.6253 7.49677C13.9777 7.49677 10.4795 8.94576 7.9003 11.525C5.32107 14.1042 3.87207 17.6024 3.87207 21.25C3.87207 24.8976 5.32107 28.3958 7.9003 30.975L10.5503 33.625L30.0003 53.075L49.4503 33.625L52.1003 30.975C53.3778 29.6981 54.3912 28.182 55.0826 26.5134C55.774 24.8447 56.1299 23.0562 56.1299 21.25C56.1299 19.4438 55.774 17.6553 55.0826 15.9866C54.3912 14.318 53.3778 12.8019 52.1003 11.525Z"
                fill="#FF3B30" stroke="#FF3B30" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" />
            </svg>
          </div>
          <p id="counter">Not active</p>
        </div>
      </div>
      <div class="container big twocols">
        <p>Sounds</p>
        <div class="tmp-container-1" tmplayer-init="main-player">
          <div class="info-wrapper">
            <div class="controls no-pointer">
              <div tmplayer-button="prev" class="prev"></div>
              <div tmplayer-button="play" class="play"></div>
              <div tmplayer-button="pause" class="pause"></div>
              <div tmplayer-button="next" class="next"></div>
            </div>
            <div class="details">
              <div tmplayer-element="title" class="title"></div>
              <div tmplayer-element="artist" class="artist"></div>
            </div>
          </div>
          <div class="playback-wrapper">
            <div class="timing-wrapper">
              <div tmplayer-element="progress-bar-wrapper" class="progress-bar-wrapper no-pointer">
                <div class="progress-bar-background">
                  <div tmplayer-element="progress-bar" class="progress-bar"></div>
                </div>
              </div>
            </div>
          </div>
          <!-- <div class="volume-container">
              <div tmplayer-button="volume-full" class="volume-full"></div>
              <div tmplayer-button="volume-half" class="volume-half"></div>
            <div tmplayer-button="volume-mute" class="volume-mute"></div>
            <div tmplayer-element="volume-bar-wrapper" class="volume-bar-wrapper">
              <div class="volume-bar-background">
                <div tmplayer-element="volume-bar" class="volume-bar"></div>
              </div>
            </div>
          </div> -->
 
          <div id="copyright-free">
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/no_copyright/conception.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Conception</div> <!-- Audio title -->
              <div tmplayer-meta="artist">David Cutter</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-38.png
              </div> <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/no_copyright/hoops.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Hoops</div> <!-- Audio title -->
              <div tmplayer-meta="artist">David Cutter</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-40.png
              </div> <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/no_copyright/appreciate_that.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Appreciate That</div> <!-- Audio title -->
              <div tmplayer-meta="artist">David Cutter</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div> <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/no_copyright/nobodys_perfect.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Nobody's Perfect</div> <!-- Audio title -->
              <div tmplayer-meta="artist">David Cutter</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div> <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/no_copyright/seeing.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Seeing</div> <!-- Audio title -->
              <div tmplayer-meta="artist">David Cutter</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div> <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/no_copyright/side_you.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Side You</div> <!-- Audio title -->
              <div tmplayer-meta="artist">David Cutter</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div> <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/no_copyright/like_this.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Like this</div> <!-- Audio title -->
              <div tmplayer-meta="artist">David Cutter</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div> <!-- Audio thumbnail -->
            </div>
          </div>
 
          <div id="copyright">
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/copyright/stan.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Stan</div> <!-- Audio title -->
              <div tmplayer-meta="artist">Eminem</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-38.png
              </div>
              <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/copyright/coffee_beam.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">COFFEE BEAM</div> <!-- Audio title -->
              <div tmplayer-meta="artist">Travis Scott</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-40.png
              </div>
              <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/copyright/lovely_day.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Lovely Day</div> <!-- Audio title -->
              <div tmplayer-meta="artist">Bill Withers</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div>
              <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/copyright/love_lost.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Love Lost</div> <!-- Audio title -->
              <div tmplayer-meta="artist">Mac Miller</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div>
              <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/copyright/marea.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Marea</div> <!-- Audio title -->
              <div tmplayer-meta="artist">Fred again..</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div>
              <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/copyright/danielle.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Danielle</div> <!-- Audio title -->
              <div tmplayer-meta="artist">Fred again..</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div>
              <!-- Audio thumbnail -->
            </div>
            <div tmplayer-element="audio" class="tap-track">
              <div tmplayer-meta="audio-url" class="track-url">./music/copyright/sevilla.mp3</div>
              <!-- Audio url (Required) -->
              <div tmplayer-meta="title">Sevilla</div> <!-- Audio title -->
              <div tmplayer-meta="artist">Henrique Camacho</div> <!-- Audio artist name -->
              <div tmplayer-meta="album">True Volume 2</div> <!-- Audio album name -->
              <div tmplayer-meta="thumbnail">https://upliftwebdesign.com/wp-content/uploads/2022/05/Rectangle-37.png
              </div>
              <!-- Audio thumbnail -->
            </div>
          </div>
        </div>
      </div>
    </div>
    <p class="smalltext">A Project by Florian Kiem and Anton Stallbörger / Softwareentwurf / IoT 3</p>
  </main>
</body>
<script src="./libs/chart.js"></script>
<script src="./libs/audioplayer.js"></script>
<script type="module" src="client.js"></script>
 
</html>

CSS

* {
    box-sizing: border-box;
}
 
body {
    background-color: #fff;
    font-family: 'Inter', sans-serif;
    font-size: 16px;
    color: rgb(0, 0, 0);
    margin: 0;
    padding: 0;
}
 
.navigation {
    width: 100%;
    display: flex;
    background-color: #fff;
    height: 64px;
    align-items: center;
    justify-content: space-between;
    padding: 0 32px;
    border-bottom: 1px solid #E5E5E5;
}
 
.logo {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 18px;
    font-weight: 600;
    text-decoration: none;
    color: black;
}
 
.logs {
    overflow: hidden;
    overflow: hidden;
    display: flex;
    flex-direction: column-reverse;
    gap: 0px;
    justify-content: flex-end;
}
 
.logo > svg {
    color: #FF3B30;
}
 
.chart {
    width: 100%;
    height: 100%;
}
 
.graph {
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: relative;
}
 
.tworows {
    position: relative;
    grid-area: log;
}
 
.twocols {
    grid-area: sounds;
    height: 100%;
    position: relative;
}
 
.menu-button {
    border: 1px solid #E5E5E5;
    border-radius: 6px;
    padding: 12px;
    font-weight: 400;
    background-color: white;
    cursor: pointer;
}
 
.menu-button:hover {
    border-color: black;
    color: #666666;
}
 
.scroll-container {
    overflow-y: scroll;
    height: 100%;
}
 
.linear-gradient-overlay {
    width: 100%;
    height: 100%;
    position: absolute;
    bottom: 0;
    left: 0;
    z-index: 1;
    background: linear-gradient(180deg, rgba(255,255,255,0) 50%, rgba(255,255,255,1) 100%);
    pointer-events: none;
    border-radius: 12px;
}
 
.tworows > div {
    height: 100%;
}
 
.box {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}
 
.graph-overlay {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    background: radial-gradient(circle, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 80%);
}
 
.main {
    width: 100vw;
    height: calc(100vh - 64px);
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}
 
.button {
    padding: 12px 24px;
    background-color: #000000;
    border-radius: 6px;
    border: none;
    cursor: pointer;
    color: white;
    font-size: 16px;
    font-family: 'Inter', sans-serif;
    display: flex;
    gap: 8px;
    align-items: center;
}
 
.button:hover {
    background-color: #fff;
    border: 1px solid black;
    color: black;
}
 
div.big {
    min-height: 270px;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    overflow: hidden;
}
 
.graph {
    width: 100%;
    height: 100%;
}
 
@media screen and (min-width: 1500px) {
    .main {
        width: 100vw;
        height: calc(100vh - 64px);
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: column;
    }
    .content {
        display: grid; 
        grid-template-columns: 420px 420px 420px; 
        grid-template-rows: auto 1fr 1fr; 
        gap: 20px 20px; 
        grid-template-areas: 
          ". . log"
          ". . log"
          "sounds sounds log"; 
        margin-bottom: 48px;
      }
}
 
@media screen and (max-width: 1500px) {
    .main {
        padding-top: 32px;
        padding-bottom: 32px;
        width: 100vw;
        height: auto;
        display: flex;
        flex-direction: column;
    }
    .logs {
        width: 100%;
    }
    .content {
        display: grid; 
        grid-template-columns: 420px 420px; 
        grid-template-rows: auto 1fr 1fr; 
        gap: 20px 20px; 
        grid-template-areas: 
          ". log"
          ". log"
          "sounds log"; 
        margin-bottom: 48px;
      }
      div.container {
        padding: 16px;
        flex-direction: column;
        align-items: flex-start;
        justify-content: flex-start;
        gap: 16px;
      }
      .smalltext {
        text-align: center;
        padding: 0 16px;
      }
 
      div.container > * {
        margin: 0;
      }
}
 
@media screen and (max-width: 900px) {
    .content {
        display: flex;
        flex-direction: column;
        gap: 20px;
    }
}
 
.log {
    margin: 0px 12px;
    border-bottom: 1px solid #E5E5E5;
    width: calc(100% - 24px);
    padding: 16px 0px;
    font-weight: 400;
    font-size: 14px;
}
 
.no-pointer {
    pointer-events: none;
}
 
.ready > .circle {
    background-color: #F5A622;
}
 
.circle {
    height: 12px;
    width: 12px;
    background-color: #FF3B30;
    border-radius: 50%;
}
 
.container {
    font-weight: 500;
    font-size: 18px;
    padding: 8px 12px;
    border-radius: 12px;
    border: 1px solid #E5E5E5;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
}
 
.container > p {
    margin-left: 12px;
}
 
.buttons {
    background-color: #fff;
    border-radius: 6px;
    display: flex;
    align-items: center;
    overflow: hidden;
}
 
button {
    font-weight: 600;
    font-size: 16px;
    font-family: 'Inter', sans-serif;
    color: #888888;
    position: relative;
}
 
button:hover {
    border-color: black;
    color:rgb(0, 0, 0);
    z-index: 1;
}
 
button:active {
    border-color: black;
    color:rgb(0, 0, 0);
    background-color: #FAFAFA;
    z-index: 1;
}
 
.smalltext {
    font-size: 12px;
    font-weight: 400;
    color: #999999;
    margin-left: 12px;
}
 
.active {
    border: 1px solid black;
    border-top-left-radius: 6px;
    border-bottom-left-radius: 6px;
    background-color: black;
    padding: 15px 20px;
}
 
.buttons > button {
    cursor: pointer;
}
 
.firstbtn {
    border: 1px solid #E5E5E5;
    border-top-left-radius: 6px;
    border-bottom-left-radius: 6px;
    background-color: #fff;
    padding: 15px 20px;
}
 
.secondbtn {
    border: 1px solid #E5E5E5;
    margin-left: -1px;
    border-top-right-radius: 6px;
    border-bottom-right-radius: 6px;
    background-color: #fff;
    padding: 15px 20px;
}
 
.activeState {
    background-color: #FAFAFA;
    border-color: #E5E5E5;
    color: black;
}
 
.heart-wrapper{
	animation: none;
    position: relative;
}
 
.heart-animation {
    animation: blur 0.7s ease-in-out infinite;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateY(-50%);
    opacity: 0.5;
    display: none;
}
 
@keyframes pulse{
	0%{
		transform:scale(1) ;
	}
	50%{
		transform:scale(1.1);
	}
	70%{
		transform:scale(1.2);
	}
	100%{
		transform:scale(1);
	}
}
 
 
@keyframes blur {
    0% {
    filter: blur(56px);
    }
    50% {
    filter: blur(96px);
    }
    70% {
    filter: blur(128px);
    }
    95% {
    filter: blur(256px);
    }
    }
 
    .tmp-container-1 {
        display: flex;
        -webkit-user-select: none;
           -moz-user-select: none;
            -ms-user-select: none;
                user-select: none;
                padding: 20px 12px;
        flex-direction: column;
        justify-content: left;
        align-items: left;
        width: 100%;
        height: 100%;
      }
      .tmp-container-1 .info-wrapper {
        display: flex;
        align-items: center;
        gap: 16px;
      }
      .tmp-container-1 .info-wrapper .thumbnail {
        height: 42px;
        width: 42px;
        border-radius: 5px;
        background-color: #434343;
        overflow: hidden;
        margin-right: 20px;
      }
      .tmp-container-1 .info-wrapper .thumbnail > img {
        height: 42px;
        width: 42px;
        -o-object-fit: cover;
           object-fit: cover;
      }
      .tmp-container-1 .info-wrapper .details .title {
        color: #000;
        font-size: 16px;
        line-height: 1.1em;
        margin-bottom: 8px;
        font-weight: 500;
      }
      .tmp-container-1 .info-wrapper .details .artist {
        color: #888888;
        font-size: 16px;
        line-height: 1.1em;
        font-weight: 500;
      }
      .tmp-container-1 .playback-wrapper {
        display: flex;
        flex-grow: 1;
        width: 100%;
        justify-content: center;
        -webkit-user-select: none;
           -moz-user-select: none;
            -ms-user-select: none;
                user-select: none;
      }
      .controls {
        display: flex;
        align-items: center;
        background-color: #000;
        border-radius: 50%;
      }
      .controls > .play, .controls > .pause {
        padding: 8px;
      }
      .controls > .play::after {
        content: '';
        display: block;
        height: 24px;
        width: 24px;
        color: #fff;
        background: url('data:image/svg+xml;utf8,<svg width="20" height="20" viewBox="0 0 20 20" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M5.755 3.78085C5.67909 3.73584 5.59262 3.71173 5.50437 3.71096C5.41612 3.71019 5.32924 3.73279 5.25255 3.77646C5.17586 3.82014 5.1121 3.88333 5.06775 3.95963C5.02339 4.03592 5.00002 4.1226 5 4.21085V15.7892C5.00002 15.8774 5.02339 15.9641 5.06775 16.0404C5.1121 16.1167 5.17586 16.1799 5.25255 16.2236C5.32924 16.2672 5.41612 16.2898 5.50437 16.2891C5.59262 16.2883 5.67909 16.2642 5.755 16.2192L15.5242 10.43C15.5988 10.3857 15.6606 10.3228 15.7035 10.2474C15.7464 10.172 15.769 10.0868 15.769 10C15.769 9.91326 15.7464 9.828 15.7035 9.7526C15.6606 9.67721 15.5988 9.61429 15.5242 9.57002L5.755 3.78085Z" fill="white" stroke="white" stroke-width="1.23333" stroke-linecap="round" stroke-linejoin="round"/></svg>') no-repeat;
        object-fit: contain;
        background-position: center;
        cursor: pointer;
        opacity: 1;
      }
      .controls > .previous::after {
        content: '';
        display: none;
        height: 16px;
        width: 16px;
        background: url('data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.5 6.83859C3.83333 7.22349 3.83333 8.18574 4.5 8.57064L11.3807 12.5432C12.0473 12.9281 12.8807 12.447 12.8807 11.6772L12.8807 3.73205C12.8807 2.96225 12.0473 2.48112 11.3807 2.86602L4.5 6.83859Z" fill="black"/><rect width="0.988716" height="11.4089" rx="0.494358" transform="matrix(-1 0 0 1 4.71875 2.00098)" fill="black" /></svg>') no-repeat;
        cursor: pointer;
        opacity: 0.7;
      }
      .controls > .next::after {
        content: '';
        display: none;
        height: 16px;
        width: 16px;
        background: url('data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M11.3809 6.83859C12.0475 7.22349 12.0475 8.18574 11.3809 8.57064L4.50018 12.5432C3.83351 12.9281 3.00018 12.447 3.00018 11.6772L3.00018 3.73205C3.00018 2.96225 3.83351 2.48112 4.50018 2.86602L11.3809 6.83859Z" fill="black"/><rect x="11.1621" y="2.00098" width="0.988716" height="11.4089" rx="0.494358" fill="black"/></svg>') no-repeat;
        cursor: pointer;
        opacity: 0.7;
      }
      .controls > .pause::after {
        content: '';
        display: block;
        height: 24px;
        width: 24px;
        background: url('data:image/svg+xml;utf8,<svg width="20" height="20" viewBox="0 0 20 20" fill="white" xmlns="http://www.w3.org/2000/svg"> <path d="M5 15.3333V4.66663C5 4.53402 5.05268 4.40684 5.14645 4.31307C5.24021 4.2193 5.36739 4.16663 5.5 4.16663H7.83333C7.96594 4.16663 8.09312 4.2193 8.18689 4.31307C8.28066 4.40684 8.33333 4.53402 8.33333 4.66663V15.3333C8.33333 15.4659 8.28066 15.5931 8.18689 15.6868C8.09312 15.7806 7.96594 15.8333 7.83333 15.8333H5.5C5.36739 15.8333 5.24021 15.7806 5.14645 15.6868C5.05268 15.5931 5 15.4659 5 15.3333ZM11.6667 15.3333V4.66663C11.6667 4.53402 11.7193 4.40684 11.8131 4.31307C11.9069 4.2193 12.0341 4.16663 12.1667 4.16663H14.5C14.6326 4.16663 14.7598 4.2193 14.8536 4.31307C14.9473 4.40684 15 4.53402 15 4.66663V15.3333C15 15.4659 14.9473 15.5931 14.8536 15.6868C14.7598 15.7806 14.6326 15.8333 14.5 15.8333H12.1667C12.0341 15.8333 11.9069 15.7806 11.8131 15.6868C11.7193 15.5931 11.6667 15.4659 11.6667 15.3333Z" fill="white" stroke="white" stroke-width="1.23333"/> </svg> ') no-repeat;
        background-position: center;
        object-fit: contain;
        cursor: pointer;
        opacity: 1;
      }
      .tmp-container-1 .playback-wrapper .timing-wrapper {
        display: flex;
        flex-grow: 1;
        align-items: center;
        max-width: 500px;
        font-size: 16px;
      }
      .tmp-container-1 .playback-wrapper .timing-wrapper .progress-bar-wrapper {
        height: 20px;
        flex-grow: 1;
        cursor: pointer;
        display: flex;
        align-items: center;
      }
      .tmp-container-1 .playback-wrapper .timing-wrapper .progress-bar-wrapper:hover .progress-bar-background .progress-bar:after {
        display: none;
      }
      .tmp-container-1 .playback-wrapper .timing-wrapper .progress-bar-wrapper .progress-bar-background {
        height: 2px;
        width: 100%;
        background-color: #D9D9D9;
        border-radius: 20px;
      }
      .tmp-container-1 .playback-wrapper .timing-wrapper .progress-bar-wrapper .progress-bar-background .progress-bar {
        height: 100%;
        width: 0%;
        background-color: #000000;
        position: relative;
        border-radius: 20px;
      }
      @media screen and (max-width: 768px) {
        .tmp-container-1 .playback-wrapper {
          margin: auto 0px;
          justify-content: flex-end;
          flex-direction: row;
        }
        .tmp-container-1 .playback-wrapper .controls {
          -moz-column-gap: 10px;
               column-gap: 10px;
        }
        .tmp-container-1 .playback-wrapper .timing-wrapper {
          display: none;
        }
      }
      .tmp-container-1 .volume-container {
        display: flex;
        align-items: center;
      }
      .tmp-container-1 .volume-container > .volume-full {
        padding: 5px;
      }
      .tmp-container-1 .volume-container > .volume-full::after {
        content: '';
        display: block;
        height: 19px;
        width: 19px;
        background: url('data:image/svg+xml;utf8,<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.0364 3.24575C9.90782 3.19013 9.7668 3.16961 9.62771 3.1863C9.48863 3.20299 9.35647 3.25629 9.24471 3.34075L5.46055 6.33325H2.57096C2.361 6.33325 2.15964 6.41665 2.01117 6.56512C1.8627 6.71359 1.7793 6.91495 1.7793 7.12491V11.8749C1.7793 12.0849 1.8627 12.2862 2.01117 12.4347C2.15964 12.5832 2.361 12.6666 2.57096 12.6666H5.46055L9.20513 15.6591C9.34442 15.7708 9.51739 15.8322 9.69596 15.8332C9.81421 15.8352 9.93113 15.808 10.0364 15.7541C10.1711 15.6899 10.2849 15.589 10.3648 15.4629C10.4446 15.3369 10.4872 15.1908 10.4876 15.0416V3.95825C10.4872 3.80903 10.4446 3.66297 10.3648 3.5369C10.2849 3.41084 10.1711 3.30991 10.0364 3.24575ZM8.9043 13.3949L6.22846 11.2574C6.08918 11.1457 5.9162 11.0843 5.73763 11.0832H3.36263V7.91658H5.73763C5.9162 7.91554 6.08918 7.85417 6.22846 7.74241L8.9043 5.60491V13.3949ZM12.1818 3.23783C12.0778 3.22328 11.972 3.22934 11.8704 3.25568C11.7688 3.28202 11.6733 3.32811 11.5895 3.39133C11.5057 3.45455 11.4352 3.53366 11.382 3.62414C11.3287 3.71462 11.2939 3.8147 11.2793 3.91866C11.2647 4.02263 11.2708 4.12844 11.2971 4.23006C11.3235 4.33168 11.3696 4.42711 11.4328 4.51092C11.5605 4.68017 11.7502 4.79177 11.9601 4.82116C13.0813 4.99394 14.1036 5.56226 14.8422 6.42333C15.5807 7.2844 15.9867 8.38134 15.9867 9.51575C15.9867 10.6502 15.5807 11.7471 14.8422 12.6082C14.1036 13.4692 13.0813 14.0376 11.9601 14.2103C11.7502 14.225 11.5546 14.3225 11.4166 14.4814C11.2785 14.6402 11.2092 14.8475 11.2239 15.0574C11.2386 15.2674 11.3361 15.4629 11.4949 15.601C11.6538 15.739 11.861 15.8084 12.071 15.7937H12.1818C13.6803 15.567 15.0478 14.8104 16.0361 13.6614C17.0243 12.5124 17.5678 11.0471 17.5678 9.53158C17.5678 8.01604 17.0243 6.55073 16.0361 5.40173C15.0478 4.25272 13.6803 3.49619 12.1818 3.2695V3.23783ZM11.8176 10.9803C11.6077 11.0139 11.4196 11.1295 11.2949 11.3018C11.1702 11.474 11.119 11.6887 11.1526 11.8987C11.1862 12.1086 11.3018 12.2966 11.4741 12.4214C11.6463 12.5461 11.861 12.5973 12.071 12.5637C12.1569 12.5627 12.2422 12.5494 12.3243 12.5241C12.941 12.3054 13.4749 11.901 13.8524 11.3666C14.2299 10.8321 14.4326 10.1938 14.4326 9.5395C14.4326 8.88515 14.2299 8.24687 13.8524 7.71242C13.4749 7.17797 12.941 6.7736 12.3243 6.55491C12.1248 6.48772 11.9068 6.50253 11.7183 6.59606C11.5297 6.68959 11.3861 6.8542 11.3189 7.05366C11.2517 7.25313 11.2665 7.47111 11.36 7.65967C11.4536 7.84822 11.6182 7.99189 11.8176 8.05908C12.118 8.17303 12.3767 8.37567 12.5592 8.64009C12.7417 8.90451 12.8394 9.2182 12.8394 9.5395C12.8394 9.86079 12.7417 10.1745 12.5592 10.4389C12.3767 10.7033 12.118 10.906 11.8176 11.0199V10.9803Z" fill="#434343"/></svg>') no-repeat;
        cursor: pointer;
      }
      .tmp-container-1 .volume-container > .volume-half {
        padding: 5px;
      }
      .tmp-container-1 .volume-container > .volume-half::after {
        content: '';
        display: block;
        height: 19px;
        width: 19px;
        background: url('data:image/svg+xml;utf8,<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10.0364 3.24575C9.90782 3.19013 9.7668 3.16961 9.62771 3.1863C9.48863 3.20299 9.35647 3.25629 9.24471 3.34075L5.46055 6.33325H2.57096C2.361 6.33325 2.15964 6.41665 2.01117 6.56512C1.8627 6.71359 1.7793 6.91495 1.7793 7.12491V11.8749C1.7793 12.0849 1.8627 12.2862 2.01117 12.4347C2.15964 12.5832 2.361 12.6666 2.57096 12.6666H5.46055L9.20513 15.6591C9.34442 15.7708 9.51739 15.8322 9.69596 15.8332C9.81421 15.8352 9.93113 15.808 10.0364 15.7541C10.1711 15.6899 10.2849 15.589 10.3648 15.4629C10.4446 15.3369 10.4872 15.1908 10.4876 15.0416V3.95825C10.4872 3.80903 10.4446 3.66297 10.3648 3.5369C10.2849 3.41084 10.1711 3.30991 10.0364 3.24575ZM8.9043 13.3949L6.22846 11.2574C6.08918 11.1457 5.9162 11.0843 5.73763 11.0832H3.36263V7.91658H5.73763C5.9162 7.91554 6.08918 7.85417 6.22846 7.74241L8.9043 5.60491V13.3949ZM15.7601 5.01908C15.6111 4.87001 15.4089 4.78626 15.198 4.78626C14.9872 4.78626 14.785 4.87001 14.636 5.01908C14.4869 5.16815 14.4031 5.37034 14.4031 5.58116C14.4031 5.79199 14.4869 5.99417 14.636 6.14325C15.1021 6.6087 15.4664 7.16603 15.7056 7.77982C15.9448 8.39362 16.0537 9.05046 16.0254 9.70861C15.9971 10.3668 15.8323 11.0118 15.5413 11.6028C15.2503 12.1938 14.8395 12.7179 14.3351 13.1416C14.2126 13.2463 14.125 13.386 14.084 13.5418C14.043 13.6977 14.0505 13.8624 14.1056 14.0138C14.1607 14.1653 14.2607 14.2963 14.3922 14.3895C14.5238 14.4826 14.6806 14.5333 14.8418 14.5349C15.0268 14.5353 15.206 14.4709 15.3485 14.3528C16.0221 13.7886 16.571 13.0904 16.9602 12.3025C17.3494 11.5147 17.5703 10.6545 17.6091 9.77658C17.6478 8.8987 17.5034 8.02237 17.1851 7.20332C16.8668 6.38427 16.3815 5.64042 15.7601 5.01908ZM13.5197 7.2595C13.4459 7.18568 13.3583 7.12713 13.2618 7.08718C13.1654 7.04723 13.062 7.02667 12.9576 7.02667C12.8532 7.02667 12.7499 7.04723 12.6534 7.08718C12.557 7.12713 12.4694 7.18568 12.3955 7.2595C12.3217 7.33331 12.2632 7.42094 12.2232 7.51738C12.1833 7.61382 12.1627 7.71719 12.1627 7.82158C12.1627 7.92597 12.1833 8.02933 12.2232 8.12578C12.2632 8.22222 12.3217 8.30985 12.3955 8.38366C12.6929 8.67923 12.8609 9.08066 12.8626 9.49991C12.8628 9.73056 12.8126 9.95847 12.7155 10.1677C12.6184 10.3769 12.4768 10.5624 12.3005 10.7112C12.2204 10.7776 12.1541 10.8592 12.1055 10.9514C12.057 11.0435 12.0271 11.1442 12.0175 11.2479C12.0079 11.3516 12.0189 11.4562 12.0498 11.5556C12.0807 11.6551 12.131 11.7474 12.1976 11.8274C12.2646 11.907 12.3467 11.9726 12.4391 12.0205C12.5315 12.0683 12.6324 12.0975 12.7361 12.1063C12.8397 12.1151 12.9441 12.1034 13.0433 12.0719C13.1424 12.0403 13.2344 11.9895 13.3139 11.9224C13.6678 11.6256 13.9526 11.255 14.1482 10.8365C14.3437 10.418 14.4454 9.96184 14.446 9.49991C14.4415 8.66083 14.1091 7.85677 13.5197 7.2595Z" fill="#434343"/></svg>') no-repeat;
        cursor: pointer;
      }
      .tmp-container-1 .volume-container > .volume-mute {
        padding: 5px;
      }
      .tmp-container-1 .volume-container > .volume-mute::after {
        content: '';
        display: block;
        height: 19px;
        width: 19px;
        background: url('data:image/svg+xml;utf8,<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.5989 3.24575C13.4703 3.19013 13.3293 3.16961 13.1902 3.1863C13.0511 3.20299 12.919 3.25629 12.8072 3.34075L9.02305 6.33325H6.13346C5.9235 6.33325 5.72214 6.41665 5.57367 6.56512C5.4252 6.71359 5.3418 6.91495 5.3418 7.12491V11.8749C5.3418 12.0849 5.4252 12.2862 5.57367 12.4347C5.72214 12.5832 5.9235 12.6666 6.13346 12.6666H9.02305L12.7676 15.6591C12.9069 15.7708 13.0799 15.8322 13.2585 15.8332C13.3767 15.8352 13.4936 15.808 13.5989 15.7541C13.7336 15.6899 13.8474 15.589 13.9273 15.4629C14.0071 15.3369 14.0497 15.1908 14.0501 15.0416V3.95825C14.0497 3.80903 14.0071 3.66297 13.9273 3.5369C13.8474 3.41084 13.7336 3.30991 13.5989 3.24575ZM12.4668 13.3949L9.79096 11.2574C9.65168 11.1457 9.4787 11.0843 9.30013 11.0832H6.92513V7.91658H9.30013C9.4787 7.91554 9.65168 7.85417 9.79096 7.74241L12.4668 5.60491V13.3949Z" fill="#434343"/></svg>') no-repeat;
        cursor: pointer;
      }
      .tmp-container-1 .volume-container img {
        width: 19px;
        height: 19px;
        cursor: pointer;
      }
      .tmp-container-1 .volume-container .volume-bar-wrapper {
        min-width: 105px;
        width: 105px;
        height: 20px;
        flex-grow: 1;
        cursor: pointer;
        margin: 0px  10px;
        display: flex;
        align-items: center;
      }
      .tmp-container-1 .volume-container .volume-bar-wrapper:hover .volume-bar-background .volume-bar:after {
        opacity: 1;
      }
      .tmp-container-1 .volume-container .volume-bar-wrapper .volume-bar-background {
        height: 2px;
        width: 100%;
        background-color: #A0A0A0;
      }
      .tmp-container-1 .volume-container .volume-bar-wrapper .volume-bar-background .volume-bar {
        height: 100%;
        width: 100%;
        background-color: #00CEB5;
        position: relative;
      }
      .tmp-container-1 .volume-container .volume-bar-wrapper .volume-bar-background .volume-bar:after {
        content: '';
        height: 12px;
        width: 12px;
        background-color: #fff;
        border-radius: 100%;
        box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.12);
        position: absolute;
        right: -5px;
        top: -5px;
        opacity: 0px;
        transition: opacity 0.2s ease;
      }
      .tmp-container-1 > .tap-track {
        display: none;
      }
      @media screen and (max-width: 1024px) {
        .tmp-container-1 .volume-container {
          display: none;
        }
      }

JavaScript

let sensorActive = false;
let reviveActive = false;
 
let count = 0;
let mode = "copyright-free";
 
let songs = [
  {
    name: "Stan",
    bpm: 80,
  },
  {
    name: "COFFEE BEAM",
    bpm: 90,
  },
  {
    name: "Lovely Day",
    bpm: 100,
  },
  {
    name: "Love Lost",
    bpm: 110,
  },
  {
    name: "Marea",
    bpm: 120,
  },
  {
    name: "Danielle",
    bpm: 130,
  },
  {
    name: "Sevilla",
    bpm: 140,
  },
];
 
let noCopyrightSongs = [
  {
    name: "Conception",
    bpm: 80,
  },
  {
    name: "Hoops",
    bpm: 90,
  },
  {
    name: "Appreciate That",
    bpm: 100,
  },
  {
    name: "Nobody's Perfect",
    bpm: 110,
  },
  {
    name: "Seeing",
    bpm: 120,
  },
  {
    name: "Side You",
    bpm: 130,
  },
  {
    name: "Like this",
    bpm: 140,
  },
];
 
function setMaxHeight() {
  let height = document.querySelector(".content").offsetHeight;
  document.querySelector(".tworows").style.maxHeight = `${height}px`;
  console.log(height);
}
 
window.onload = setMaxHeight();
 
const getData = async () => {
  if (sensorActive) {
    try {
      const response = await fetch("/db", { method: "GET" });
      if (response.ok) {
        const data = await response.json();
        console.log(data.recordset);
 
        // create a log div with the newest pulse value and append it to the log div if it is different from the last one
        let logDiv = document.createElement("div");
        logDiv.classList.add("log");
        logDiv.innerHTML = `${data.recordset[0].pulseSensor} BPM`;
        if (document.getElementById("logs").lastChild) {
          if (
            document.getElementById("logs").lastChild.innerHTML !==
            logDiv.innerHTML
          ) {
            document.getElementById("logs").appendChild(logDiv);
          }
        } else {
          document.getElementById("logs").appendChild(logDiv);
        }
 
        if (
          data.recordset[0].pulseSensor > 30 &&
          data.recordset[0].pulseSensor < 200
        ) {
          document.getElementById(
            "counter"
          ).innerHTML = `Pulse: ${data.recordset[0].pulseSensor}`;
        } else {
          document.getElementById("counter").innerHTML = `No pulse detected`;
        }
 
        document.querySelector(".heart-wrapper").style.animation =
          "pulse 0.7s ease-in-out infinite";
        document.querySelector(".heart-animation").style.display = "block";
        // array with the first 10 most recent pulse values in the right order
        let pulseArray = [];
        for (let i = 0; i < 10; i++) {
          pulseArray.push(data.recordset[i].pulseSensor);
        }
        pulseArray.reverse();
 
        return pulseArray;
      }
    } catch (error) {
      console.log(error);
    }
  } else {
    console.log("sensor is not active");
    return [65, 59, 81, 56, 55, 40];
  }
};
 
let start = document.getElementById("start");
let stop = document.getElementById("stop");
 
var data = {
  labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"],
  datasets: [
    {
      label: false,
      backgroundColor: "transparent",
      borderColor: "black",
      borderWidth: 2,
      data: [65, 59, 81, 56, 55, 40],
    },
  ],
};
 
// do not show any grid lines, labels just the graph
var options = {
  scales: {
    xAxes: [
      {
        display: false,
        gridLines: {
          display: false,
        },
      },
    ],
    yAxes: [
      {
        display: false,
        gridLines: {
          display: false,
        },
      },
    ],
  },
  legend: {
    display: false,
  },
  tooltips: {
    enabled: false,
  },
  elements: {
    point: {
      radius: 0,
    },
  },
  animations: {
    tension: {
      duration: 1000,
      easing: "linear",
      from: 1,
      to: 0,
      loop: true,
    },
  },
};
 
stop.addEventListener("click", () => {
  fetch("/editSensor", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify("off"),
  })
    .then((response) => response.json())
    .then((data) => {
      fetch("/turnMotorOff", { method: "POST" })
        .then((response) => response.json())
        .then((data) => {
          sensorActive = false;
          start.classList.remove("activeState");
          stop.classList.add("activeState");
 
          count = 0;
 
          document.querySelector(".controls").classList.add("no-pointer");
          document
            .querySelector(".progress-bar-wrapper")
            .classList.add("no-pointer");
 
          if (document.querySelector(".play").style.display === "none") {
            document.querySelector(".pause").click();
          }
 
          document.querySelector(".heart-wrapper").style.animation = "none";
          document.querySelector(".heart-animation").style.display = "none";
 
          reviveButton.classList.add("no-pointer");
          reviveButton.classList.remove("ready");
          reviveText.innerText = "Inactive";
          circle.style.backgroundColor = "#FF3B30";
 
          // Log that the pulse sensor was deactivated
          let logDiv = document.createElement("div");
          logDiv.classList.add("log");
          logDiv.innerHTML = `Pulse sensor deactivated`;
          document.getElementById("logs").appendChild(logDiv);
        });
    });
});
 
let reviveButton = document.getElementById("revive");
let reviveText = document.getElementById("reviveText");
let circle = document.getElementById("circle");
 
reviveButton.addEventListener("mousedown", () => {
  reviveActive = true;
  circle.style.backgroundColor = "#4FE3C2";
  reviveText.innerText = "Active";
 
  // Log that the message was sent
  let logDiv = document.createElement("div");
  logDiv.classList.add("log");
  logDiv.innerHTML = `Heart revived`;
  document.getElementById("logs").appendChild(logDiv);
 
  if (reviveActive) {
    console.log("revive active");
    // send message to IoT Hub
    async function sendMessage() {
      const response = await fetch("/sendMessage", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(
          document.getElementById("counter").innerText.toString()
        ),
      });
      if (response.ok) {
        console.log("message sent");
      }
    }
    sendMessage();
  }
});
 
reviveButton.addEventListener("mouseup", () => {
  reviveActive = false;
  reviveText.innerText = "Ready";
  circle.style.backgroundColor = "#F5A622";
});
 
let chart = new Chart("chart", {
  type: "line",
  options: options,
  data: data,
  scales: {
    xAxes: [
      {
        type: "time",
        time: {
          unit: "minute",
        },
      },
    ],
  },
});
 
start.addEventListener("click", () => {
  fetch("/editSensor", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify("on"),
  })
    .then((response) => response.json())
    .then((data) => {
      console.log(data);
 
      sensorActive = true;
      stop.classList.remove("activeState");
      start.classList.add("activeState");
      console.log("sensor is active");
 
      document.querySelector(".controls").classList.remove("no-pointer");
      document
        .querySelector(".progress-bar-wrapper")
        .classList.remove("no-pointer");
      reviveButton.classList.remove("no-pointer");
      reviveButton.classList.add("ready");
      reviveText.innerText = "Ready";
      circle.style.backgroundColor = "#F5A622";
 
      // Log that the pulse sensor was activated
      let logDiv = document.createElement("div");
      logDiv.classList.add("log");
      logDiv.innerHTML = `Pulse sensor activated`;
      document.getElementById("logs").appendChild(logDiv);
 
      // update chart every 5 seconds
      if (count === 0) {
        count++;
        setInterval(() => {
          if (sensorActive) {
            async function updateChart() {
              chart.data.datasets[0].data = await getData();
              chart.update();
 
              let currentBPM = chart.data.datasets[0].data[9];
 
              // round the BPM to the nearest integer in 10s range, if its lower than 80, set it to 80 and if its higher than 140, set it to 140
              currentBPM = Math.round(currentBPM / 10) * 10;
              if (currentBPM < 80) {
                currentBPM = 80;
              } else if (currentBPM > 140) {
                currentBPM = 140;
              }
 
              // search for the BPM in the songs
              let song;
 
              if (mode === "normal") {
                song = songs.find((song) => song.bpm === currentBPM);
              } else if (mode === "copyright-free") {
                song = noCopyrightSongs.find((song) => song.bpm === currentBPM);
              }
 
              // if the song is found, click the document.querySelector('.next') button until the song is playing, you can compare which song is player by checking the document.querySelector('.title').innerText
              if (
                song &&
                document.querySelector(".play").style.display === "none"
              ) {
                console.log("song found: ", song.name);
                while (
                  document.querySelector(".title").innerText !== song.name
                ) {
                  document.querySelector(".next").click();
                }
              }
            }
            updateChart();
            console.log("chart updated");
          }
        }, 2500);
      }
    });
});

Backend

//set up a nodejs server with express
const express = require("express");
const sql = require("mssql");
const app = express();
const server = require("http").createServer(app);
const port = process.env.PORT || 3000;
 
const config = {
  user: "florian",
  password: "********",
  server: "heartbeat-db.database.windows.net",
  port: 1433,
  database: "heartbeat-db",
  authentication: {
    type: "default",
  },
  options: {
    encrypt: true,
  },
};
 
// Routing
app.use(express.static(__dirname + "/public"));
 
//Rest API
app.get("/", function (req, res) {
  res.sendFile(__dirname + "/index.html");
});
 
// Get data from database
app.get("/db", (req, res) => {
  sql.connect(config, function (err) {
    if (err) console.log(err);
 
    var request = new sql.Request();
 
    request.query(
      "SELECT TOP (100) * FROM [dbo].[heartbeat-sensor] ORDER BY EventEnqueuedUtcTime DESC",
      function (err, recordset) {
        if (err) console.log(err);
 
        res.status(200).json(recordset);
      } 
    );
  });
});
 
app.post("/turnMotorOff", (req, res) => {
  var Client = require("azure-iothub").Client;
  var Message = require("azure-iot-common").Message;
 
  // Connection string for the IoT Hub
  var connectionString =
    "HostName=heartbeat-iot-hub.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=CDwDykgmyKiM2DP+pEdLpC+c1qqtfIBt0zH3Q+5s/mk=";
  var targetDevice = "heartbeat-motor";
 
  var client = Client.fromConnectionString(connectionString);
 
  client.open(function (err) {
    if (err) {
      console.error("Could not connect: " + err.message);
    } else {
      console.log("Client connected");
      // Create a message and send it to the IoT Hub every second
      var data = JSON.stringify({ "pulse": "0" });
      var message = new Message(data);
      console.log("Sending test: " + data);
      client.send(targetDevice, message, printResultFor("send"));
    }
  });
 
  function printResultFor(op) {
    return function printResult(err, resLokal) {
      if (err) {
        console.log(op + " error: " + err.toString());
      } else {
        console.log(op + " status: " + resLokal.constructor.name);
        
        res.status(201).json(op + " status: " + resLokal.constructor.name);
      }
    };
  }
});
 
app.post("/editSensor", (req, res) => {
 
  req.on("data", (data) => {
    let state = JSON.parse(data);
    console.log(state);
 
    var Client = require("azure-iothub").Client;
    var Message = require("azure-iot-common").Message;
 
    // Connection string for the IoT Hub
    var connectionString =
      "HostName=heartbeat-iot-hub.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=CDwDykgmyKiM2DP+pEdLpC+c1qqtfIBt0zH3Q+5s/mk=";
    var targetDevice = "heartbeat-sensor";
 
    var client = Client.fromConnectionString(connectionString);
 
    client.open(function (err) {
      if (err) {
        console.error("Could not connect: " + err.message);
      } else {
        console.log("Client connected");
        // Create a message and send it to the IoT Hub every second
        var data = JSON.stringify({ "device": state });
        var message = new Message(data);
        console.log("Sending test: " + state);
        client.send(targetDevice, message, printResultFor("send"));
      }
    });
 
    // Helper function to print results in the console
    function printResultFor(op) {
      return function printResult(err, resLokal) {
        if (err) {
          console.log(op + " error: " + err.toString());
        } else {
          console.log(op + " status: " + resLokal.constructor.name);
          
          res.status(201).json(op + " status: " + resLokal.constructor.name);
        }
      };
    }
 
  });
});
 
// Send message to IoT Hub
app.post("/sendMessage", (req, res) => {
 
  let pulse;
  
  req.on("data", (data) => {
    data = JSON.parse(data);
    pulse = data;
 
    // pulse without string just number
    pulse = pulse.replace(/[^0-9]/g, "");
 
    console.log("String: ", pulse);
 
      var Client = require("azure-iothub").Client;
      var Message = require("azure-iot-common").Message;
 
      // Connection string for the IoT Hub
      var connectionString =
        "HostName=heartbeat-iot-hub.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=CDwDykgmyKiM2DP+pEdLpC+c1qqtfIBt0zH3Q+5s/mk=";
      var targetDevice = "heartbeat-motor";
 
      var client = Client.fromConnectionString(connectionString);
 
      client.open(function (err) {
        if (err) {
          console.error("Could not connect: " + err.message);
        } else {
          console.log("Client connected");
 
          // Create a message and send it to the IoT Hub every second
          var data = JSON.stringify({ "pulse": pulse });
          var message = new Message(data);
          console.log("Sending message: " + message.getData());
          client.send(targetDevice, message, printResultFor("send"));
        }
      });
 
      // Helper function to print results in the console
      function printResultFor(op) {
        return function printResult(err, resLokal) {
          if (err) {
            console.log(op + " error: " + err.toString());
          } else {
            console.log(op + " status: " + resLokal.constructor.name);
            res.status(201).json(op + " status: " + resLokal.constructor.name);
          }
        };
      }
  });
});
 
server.listen(port, function () {
  console.log("Server listening at port %d", port);
});
Last updated on February 2, 2023