adds some icons to the UI

This commit is contained in:
Lars Berning 2021-03-27 03:54:58 +00:00
parent 1c135a0c1f
commit 61c3d5be8d
8 changed files with 122 additions and 103 deletions

View File

@ -23,9 +23,11 @@ export function createApp () {
const standalone = (window.location.hash === '#:standalone:')
if (standalone) {
document.getElementById('closeButton').style.display = 'inline-block'
document.getElementById('close-button').style.display = 'inline-block'
document.getElementById('fullscreen-button').style.display = 'none'
} else {
document.getElementById('fullscreenButton').style.display = 'inline-block'
document.getElementById('fullscreen-button').style.display = 'inline-block'
document.getElementById('close-button').style.display = 'none'
}
let socket
@ -40,7 +42,7 @@ export function createApp () {
socket = new WebSocket(`ws://${location.host}/websocket`)
socket.addEventListener('open', (event) => {
console.log('websocket openend')
console.log('websocket opened')
})
socket.addEventListener('error', (error) => {
@ -86,19 +88,10 @@ export function createApp () {
// In this case we use the good old hacky way of keeping the screen on via a hidden video.
// eslint-disable-next-line no-undef
const noSleep = new NoSleep()
checkAlwaysOn()
document.addEventListener('click', function enableNoSleep () {
document.removeEventListener('click', enableNoSleep, false)
noSleep.enable().then(checkAlwaysOn)
noSleep.enable()
}, false)
function checkAlwaysOn () {
if (noSleep.isEnabled) {
document.getElementById('alwaysOnHint').style.display = 'none'
} else {
document.getElementById('alwaysOnHint').style.display = 'grid'
}
}
}
function resetFields () {

View File

@ -18,65 +18,118 @@
<script type="module" src="/index.js"></script>
<div id="dashboard" class="grid">
<div class="col">
<div class="label">Distance</div>
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M416 320h-96c-17.6 0-32-14.4-32-32s14.4-32 32-32h96s96-107 96-160-43-96-96-96-96 43-96 96c0 25.5 22.2 63.4 45.3 96H320c-52.9 0-96 43.1-96 96s43.1 96 96 96h96c17.6 0 32 14.4 32 32s-14.4 32-32 32H185.5c-16 24.8-33.8 47.7-47.3 64H416c52.9 0 96-43.1 96-96s-43.1-96-96-96zm0-256c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zM96 256c-53 0-96 43-96 96s96 160 96 160 96-107 96-160-43-96-96-96zm0 128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"></path></svg>
</div>
<div class="content">
<span class="value" id="distanceTotal"></span>
<span class="unit" id="distanceTotalUnit">m</span>
<span class="metric-value" id="distanceTotal"></span>
<span class="metric-unit" id="distanceTotalUnit">m</span>
</div>
</div>
<div class="col">
<div class="label">Split</div>
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M432 304c0 114.9-93.1 208-208 208S16 418.9 16 304c0-104 76.3-190.2 176-205.5V64h-28c-6.6 0-12-5.4-12-12V12c0-6.6 5.4-12 12-12h120c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12h-28v34.5c37.5 5.8 71.7 21.6 99.7 44.6l27.5-27.5c4.7-4.7 12.3-4.7 17 0l28.3 28.3c4.7 4.7 4.7 12.3 0 17l-29.4 29.4-.6.6C419.7 223.3 432 262.2 432 304zm-176 36V188.5c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12V340c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path></svg>
</div>
<div class="content">
<span class="value" id="splitFormatted"></span>
<span class="unit">/500m</span>
<span class="metric-value" id="splitFormatted"></span>
<span class="metric-unit">/500m</span>
</div>
</div>
<div class="col">
<div class="label">Power</div>
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M296 160H180.6l42.6-129.8C227.2 15 215.7 0 200 0H56C44 0 33.8 8.9 32.2 20.8l-32 240C-1.7 275.2 9.5 288 24 288h118.7L96.6 482.5c-3.6 15.2 8 29.5 23.3 29.5 8.4 0 16.4-4.4 20.8-12l176-304c9.3-15.9-2.2-36-20.7-36z"></path></svg>
</div>
<div class="content">
<span class="value" id="power"></span>
<span class="unit">watt</span>
<span class="metric-value" id="power"></span>
<span class="metric-unit">watt</span>
</div>
</div>
<div class="col">
<div class="label">SPM</div>
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 303.46 303.46">
<path fill="currentColor" d="m254.8 278.23-103.07-103.07-103.07 103.07c-18.349 0.91143-22.401-4.2351-21.213-21.213l103.07-103.07-19.755-19.755c-20.183-1.639-63.38-27.119-70.631-34.37l-30.474-30.474c-5.224-5.224-8.101-12.168-8.101-19.555s2.877-14.332 8.102-19.555l18.365-18.365c5.223-5.224 12.167-8.1 19.554-8.1s14.331 2.876 19.554 8.1l30.475 30.475c6.162 6.163 16.762 25.271 22.383 36.609 7.431 14.991 11.352 25.826 11.979 34.014l19.763 19.763 19.762-19.762c0.627-8.188 4.548-19.023 11.98-34.015 5.621-11.34 16.221-30.447 22.383-36.609l30.475-30.475c5.223-5.224 12.167-8.1 19.554-8.1s14.331 2.876 19.554 8.1l18.366 18.366c10.781 10.781 10.782 28.325 1e-3 39.107l-30.476 30.475c-7.25 7.252-50.443 32.731-70.63 34.37l-19.756 19.756 103.07 103.07c-1 17.227-4.2977 21.56-21.214 21.213z"/>
</svg>
</div>
<div class="content">
<span class="value" id="strokesPerMinute"></span>
<span class="unit"></span>
<span class="metric-value" id="strokesPerMinute"></span>
<span class="metric-unit">/min</span>
</div>
</div>
<div class="col">
<div class="label">Strokes</div>
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 303.46 303.46">
<path fill="currentColor" d="m254.8 278.23-103.07-103.07-103.07 103.07c-18.349 0.91143-22.401-4.2351-21.213-21.213l103.07-103.07-19.755-19.755c-20.183-1.639-63.38-27.119-70.631-34.37l-30.474-30.474c-5.224-5.224-8.101-12.168-8.101-19.555s2.877-14.332 8.102-19.555l18.365-18.365c5.223-5.224 12.167-8.1 19.554-8.1s14.331 2.876 19.554 8.1l30.475 30.475c6.162 6.163 16.762 25.271 22.383 36.609 7.431 14.991 11.352 25.826 11.979 34.014l19.763 19.763 19.762-19.762c0.627-8.188 4.548-19.023 11.98-34.015 5.621-11.34 16.221-30.447 22.383-36.609l30.475-30.475c5.223-5.224 12.167-8.1 19.554-8.1s14.331 2.876 19.554 8.1l18.366 18.366c10.781 10.781 10.782 28.325 1e-3 39.107l-30.476 30.475c-7.25 7.252-50.443 32.731-70.63 34.37l-19.756 19.756 103.07 103.07c-1 17.227-4.2977 21.56-21.214 21.213z"/>
</svg>
</div>
<div class="content">
<span class="value" id="strokesTotal"></span>
<span class="unit"></span>
<span class="metric-value" id="strokesTotal"></span>
<span class="metric-unit">total</span>
</div>
</div>
<!--
<div class="col">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M320.2 243.8l-49.7 99.4c-6 12.1-23.4 11.7-28.9-.6l-56.9-126.3-30 71.7H60.6l182.5 186.5c7.1 7.3 18.6 7.3 25.7 0L451.4 288H342.3l-22.1-44.2zM473.7 73.9l-2.4-2.5c-51.5-52.6-135.8-52.6-187.4 0L256 100l-27.9-28.5c-51.5-52.7-135.9-52.7-187.4 0l-2.4 2.4C-10.4 123.7-12.5 203 31 256h102.4l35.9-86.2c5.4-12.9 23.6-13.2 29.4-.4l58.2 129.3 49-97.9c5.9-11.8 22.7-11.8 28.6 0l27.6 55.2H481c43.5-53 41.4-132.3-7.3-182.1z"></path>
</svg>
</div>
<div class="content">
<span class="metric-value" id="heartRate"></span>
<span class="metric-unit">bpm</span>
</div>
</div>
-->
<div class="col">
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M323.56 51.2c-20.8 19.3-39.58 39.59-56.22 59.97C240.08 73.62 206.28 35.53 168 0 69.74 91.17 0 209.96 0 281.6 0 408.85 100.29 512 224 512s224-103.15 224-230.4c0-53.27-51.98-163.14-124.44-230.4zm-19.47 340.65C282.43 407.01 255.72 416 226.86 416 154.71 416 96 368.26 96 290.75c0-38.61 24.31-72.63 72.79-130.75 6.93 7.98 98.83 125.34 98.83 125.34l58.63-66.88c4.14 6.85 7.91 13.55 11.27 19.97 27.35 52.19 15.81 118.97-33.43 153.42z"></path></svg>
</div>
<div class="content">
<span class="metric-value" id="caloriesTotal"></span>
<span class="metric-unit">kcal</span>
</div>
</div>
<div class="col">
<div class="label">Calories</div>
<div class="content">
<span class="value" id="caloriesTotal"></span>
<span class="unit">kcal</span>
<div class="label">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z"></path></svg>
</div>
</div>
<div class="col">
<div class="label">Duration</div>
<div class="content">
<span class="value" id="durationTotalFormatted"></span>
<span class="unit"></span>
<span class="metric-value" id="durationTotalFormatted"></span>
<span class="metric-unit"></span>
</div>
</div>
<div class="col">
<div class="content">
<button onclick="app.toggleFullscreen()" id="fullscreenButton">Fullscreen</button>
<button onclick="app.close()" id="closeButton">Exit</button>
<button onclick="app.switchPeripheralMode()" id="peripheralMode">Bluetooth Mode</button>
<button onclick="app.reset()">Reset</button>
<button onclick="app.reset()">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M212.333 224.333H12c-6.627 0-12-5.373-12-12V12C0 5.373 5.373 0 12 0h48c6.627 0 12 5.373 12 12v78.112C117.773 39.279 184.26 7.47 258.175 8.007c136.906.994 246.448 111.623 246.157 248.532C504.041 393.258 393.12 504 256.333 504c-64.089 0-122.496-24.313-166.51-64.215-5.099-4.622-5.334-12.554-.467-17.42l33.967-33.967c4.474-4.474 11.662-4.717 16.401-.525C170.76 415.336 211.58 432 256.333 432c97.268 0 176-78.716 176-176 0-97.267-78.716-176-176-176-58.496 0-110.28 28.476-142.274 72.333h98.274c6.627 0 12 5.373 12 12v48c0 6.627-5.373 12-12 12z"></path>
</svg>
</button>
<button onclick="app.toggleFullscreen()" id="fullscreen-button">
<div id="fullscreen-icon">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M0 180V56c0-13.3 10.7-24 24-24h124c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12H64v84c0 6.6-5.4 12-12 12H12c-6.6 0-12-5.4-12-12zM288 44v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12V56c0-13.3-10.7-24-24-24H300c-6.6 0-12 5.4-12 12zm148 276h-40c-6.6 0-12 5.4-12 12v84h-84c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24V332c0-6.6-5.4-12-12-12zM160 468v-40c0-6.6-5.4-12-12-12H64v-84c0-6.6-5.4-12-12-12H12c-6.6 0-12 5.4-12 12v124c0 13.3 10.7 24 24 24h124c6.6 0 12-5.4 12-12z"></path>
</svg>
</div>
<div id="windowed-icon">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M436 192H312c-13.3 0-24-10.7-24-24V44c0-6.6 5.4-12 12-12h40c6.6 0 12 5.4 12 12v84h84c6.6 0 12 5.4 12 12v40c0 6.6-5.4 12-12 12zm-276-24V44c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v84H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h124c13.3 0 24-10.7 24-24zm0 300V344c0-13.3-10.7-24-24-24H12c-6.6 0-12 5.4-12 12v40c0 6.6 5.4 12 12 12h84v84c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12zm192 0v-84h84c6.6 0 12-5.4 12-12v-40c0-6.6-5.4-12-12-12H312c-13.3 0-24 10.7-24 24v124c0 6.6 5.4 12 12 12h40c6.6 0 12-5.4 12-12z"></path>
</svg>
</div>
</button>
<button onclick="app.close()" id="close-button">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path fill="currentColor" d="M400 54.1c63 45 104 118.6 104 201.9 0 136.8-110.8 247.7-247.5 248C120 504.3 8.2 393 8 256.4 7.9 173.1 48.9 99.3 111.8 54.2c11.7-8.3 28-4.8 35 7.7L162.6 90c5.9 10.5 3.1 23.8-6.6 31-41.5 30.8-68 79.6-68 134.9-.1 92.3 74.5 168.1 168 168.1 91.6 0 168.6-74.2 168-169.1-.3-51.8-24.7-101.8-68.1-134-9.7-7.2-12.4-20.5-6.5-30.9l15.8-28.1c7-12.4 23.2-16.1 34.8-7.8zM296 264V24c0-13.3-10.7-24-24-24h-32c-13.3 0-24 10.7-24 24v240c0 13.3 10.7 24 24 24h32c13.3 0 24-10.7 24-24z"></path>
</svg>
</button>
<button onclick="app.switchPeripheralMode()">
<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<path fill="currentColor" d="M292.6 171.1L249.7 214l-.3-86 43.2 43.1m-43.2 219.8l43.1-43.1-42.9-42.9-.2 86zM416 259.4C416 465 344.1 512 230.9 512S32 465 32 259.4 115.4 0 228.6 0 416 53.9 416 259.4zm-158.5 0l79.4-88.6L211.8 36.5v176.9L138 139.6l-27 26.9 92.7 93-92.7 93 26.9 26.9 73.8-73.8 2.3 170 127.4-127.5-83.9-88.7z"></path>
</svg>
</button>
<div class="metric-unit" id="peripheralMode"></div>
</div>
</div>
</div>
<div class="popup" id="alwaysOnHint">
<p>Tap the screen to prevent display sleep.</p>
</div>
</body>
</html>

View File

@ -2,7 +2,7 @@
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
*/
body {
background-color: #000d1b;
background-color: #00091c;
color: white;
margin: 0;
font-size: calc(16px + (60 - 16) * ((100vw - 300px) / (1920 - 300)));
@ -36,47 +36,50 @@ body {
}
}
div.label, div.content {
padding: 5%;
#windowed-icon {
display: none;
}
span.value {
@media (display-mode: fullscreen) {
#fullscreen-icon {
display: none;
}
#windowed-icon {
display: inline;
}
}
div.label, div.content {
padding: 5% 1%;
}
.icon {
height: 1.8em;
}
.metric-value {
font-size: 150%;
}
span.unit {
.metric-unit {
font-size: 80%;
}
button {
outline:none;
background-color: #00468c;
/* background-color: #00468c; */
background-color: #365080;
border: 0;
color: white;
padding: 1.2vw 1.5vw;
margin: 0.8vw;
padding: 0.6em 0.9em 0.4em 0.9em;
margin: 0.2em;
font-size: 70%;
text-align: center;
text-decoration: none;
display: inline-block;
width: 3.5em;
}
#fullscreenButton, #closeButton {
#close-button {
display:none;
}
.popup {
display: none;
position: fixed;
border: solid 0.3vw white;
align-items: center;
justify-content: center;
padding: 10px;
width: 50vw;
height: 10vh;
left: 24vw;
top: 5vw;
background: rgba(80, 0, 0, 0.95);
font-size: 60%;
z-index: 20;
}

View File

@ -6,6 +6,8 @@ Open Rowing Monitor uses some great work by others. Thank you for all the great
* Dave Vernooy's project description on [ErgWare](https://dvernooy.github.io/projects/ergware) has some good information on the maths involved in a rowing ergometer.
* Bluetooth is quite a complex beast, luckily the Bluetooth SIG releases all the [specifications here](https://www.bluetooth.com/specifications/specs)
* The app icon is based on this [image of a rowing machine](https://thenounproject.com/term/rowing-machine/659265) by [Gan Khoon Lay](https://thenounproject.com/leremy/) licensed under [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/).
* Bluetooth is quite a complex beast, luckily the Bluetooth SIG releases all the [specifications here](https://www.bluetooth.com/specifications/specs)
* The frontend uses some icons from [Font Awesome](https://fontawesome.com/), licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).

View File

@ -7,6 +7,7 @@ This is the very minimalistic Backlog for further development of this project.
* investigate: occasionally stroke rate is too high - seems to happen after rowing pause
* figure out where to set the Service Advertising Data (FTMS.pdf p 15)
* set up a Raspberry Pi with the installation instructions to see if they are correct
* record a longer rowing session and analyze two encountered problems: 1) rarely the stroke rate doubles for a short duration (might be a problem with stroke detection when measurements are imprecise), 2) in one occasion the measured power jumped to a very high value after a break (40000 watts)
## Later

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 266 KiB

29
package-lock.json generated
View File

@ -1080,12 +1080,6 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
"dev": true
},
"expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
@ -1211,12 +1205,6 @@
"integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==",
"dev": true
},
"follow-redirects": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz",
"integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==",
"dev": true
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@ -1454,17 +1442,6 @@
"toidentifier": "1.0.0"
}
},
"http-proxy": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
"integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"dev": true,
"requires": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
"requires-port": "^1.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@ -2767,12 +2744,6 @@
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true
},
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",

View File

@ -23,9 +23,6 @@
"test": "uvu",
"test:watch": "uvu --watch"
},
"ava": {
"verbose": true
},
"dependencies": {
"@abandonware/bleno": "^0.5.1-3",
"finalhandler": "^1.1.2",
@ -42,7 +39,6 @@
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"http-proxy": "^1.18.1",
"husky": "^5.1.3",
"markdownlint-cli": "^0.27.1",
"npm-run-all": "^4.1.5",