Skip to content

Commit

Permalink
service worker, basic logo, webmanifest, fixing some bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
dvmarinoff committed Jan 22, 2021
1 parent 13dbd3c commit 37c285c
Show file tree
Hide file tree
Showing 19 changed files with 381 additions and 54 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,35 @@ It is not tested or supported by this project, but you can give it try if your o

## Supported Hardware

Works with all trainers that implement the Fitness Machine (FTMS) bluetooth Profile or simply put Bluetooth Smart.
Works with all trainers that implement the bluetooth Fitness Machine Service (FTMS).

List of known trainers that implement FTMS:

- Tacx Flux 1 / S / 2
- Tacx Flow Smart

- Elite Direto
- Elite Suito
- Elite Tuo
- Elite Zumo

- Saris H3
- Saris M2

- Wahoo Kickr Core
<!-- - Wahoo Snap -->

List of major trainers without FTMS:

Tacx Neo / 2 / 2T (using custom bluetooth solution)
Wahoo Kickr (using custom bluetooth solution)



#### ANT+ FE-C

I am currently researching options for FE-C on the web. It just might work.



## The Demo
Expand Down
117 changes: 89 additions & 28 deletions ble/cps.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

const cyclingPowerMeasurment =
[
{name: 'Flags',
Expand Down Expand Up @@ -48,36 +47,79 @@ const cyclingPowerMeasurment =
format: 'Int16', unit: 'Watt', decimalExponent: 0,
informativeText: 'Unit is in watts with a resolution of 1.',
},
{name: 'Pedal Power Balance', requirement: 'Optional',
{name: 'PedalPowerBalance', requirement: 'Optional',
format: 'Uint8', unit: 'Percentage', binaryExponent: -1,
informativeText: 'Unit is in percentage with a resolution of 1/2.',
},
{name: '',
requirement: '',
format: '',
unit: '',
decimalExponent: 0,
informativeText: '',
{name: 'AccumulatedTorque', requirement: 'Optional',
format: 'Uint16',
unit: 'N m',
binaryExponent: -5,
informativeText: 'Unit is in newton metres with a resolution of 1/32.',
},

{name: 'WheelRevolutionDataCumulativeWheelRevolutions', requirement: 'C1',
format: 'Uint32', unit: 'unitless', decimalExponent: 0,
informativeText: 'Unitless. C1:When present, these fields are always present as a pair.',
},
{name: 'WheelRevolutionDataLastWheelEventTime', requirement: 'C1',
format: 'Uint16', unit: 'second', binaryExponent: -11,
informativeText: 'Unit is in seconds with a resolution of 1/2048. C1:When present, these fields are always present as a pair.',
},

{name: 'CrankRevolutionDataCumulativeCrankRevolutions', requirement: 'C2',
format: 'Uint16', unit: 'unitless', decimalExponent: 0,
informativeText: 'Unitless. C2:When present, these fields are always present as a pair.',
},
{name: 'CrankRevolutionDataLastCrankEventTime', requirement: 'C3',
format: 'Int16', unit: 'N', binaryExponent: -10,
informativeText: 'Unit is in newtons with a resolution of 1. C3:When present, these fields are always present as a pair.',
},


{name: 'ExtremeForceMagnitudesMinimumForceMagnitude', requirement: 'C3',
format: 'Int16', unit: 'N', decimalExponent: 0,
informativeText: 'Unit is in newtons with a resolution of 1. C3:When present, these fields are always present as a pair.',
},
{name: 'ExtremeTorqueMagnitudesMaximumTorqueMagnitude', requirement: 'C4',
format: 'Int16', unit: 'N m', binaryExponent: -5,
informativeText: 'Unit is in newton metres with a resolution of 1/32. C4:When present, these fields are always present as a pair.',
},
{name: 'ExtremeTorqueMagnitudesMinimumTorqueMagnitude', requirement: 'C4',
format: 'Int16', unit: 'N m', binaryExponent: -5,
informativeText: 'Unit is in newton metres with a resolution of 1/32. C4:When present, these fields are always present as a pair.',
},

{name: '',
requirement: '',
format: '',
unit: '',
decimalExponent: 0,
informativeText: '',
{name: 'ExtremeAnglesMaximumAngle', requirement: 'C5',
format: 'Uint12', unit: 'degree', decimalExponent: 0,
informativeText: `Unit is in degrees with a resolution of 1.
C5: When present, this field and the "Extreme Angles - Minimum Angle" field are always present as a
pair and are concatenated into a UINT24 value (3 octets). As an example, if the Maximum Angle is
0xABC and the Minimum Angle is 0x123, the transmitted value is 0x123ABC.`,
},
{name: '',
requirement: '',
format: '',
unit: '',
decimalExponent: 0,
informativeText: '',

{name: 'ExtremeAnglesMinimumAngle', requirement: 'C5',
format: 'Uint12', unit: 'degree', decimalExponent: 0,
informativeText: `Unit is in degrees with a resolution of 1.
C5: When present, this field and the "Extreme Angles - Maximum Angle" field are always present as a
pair and are concatenated into a UINT24 value (3 octets). As an example, if the Maximum Angle is
0xABC and the Minimum Angle is 0x123, the transmitted value is 0x123ABC.`,
},

{name: 'TopDeadSpotAngle', requirement: 'Optional',
format: 'Uint16', unit: 'degree', decimalExponent: 0,
informativeText: 'Unit is in degrees with a resolution of 1.',
},

// {name: 'Flags',
// requirement: 'Mandatory',
// format: 'Uint16',
{name: 'BottomDeadSpotAngle', requirement: 'Optional',
format: 'Uint16', unit: 'degree', binaryExponent: 0,
informativeText: 'Unit is in degrees with a resolution of 1.',
},

{name: 'AccumulatedEnergy', requirement: 'Optional',
format: 'Uint16', unit: 'J', binaryExponent: 0,
informativeText: 'Unit is in kilojoules with a resolution of 1.',
},
];

let cyclingPowerFeatureSupport =
Expand Down Expand Up @@ -123,25 +165,44 @@ let cyclingPowerMeasurementFlags =



function flagsToFieldsPresent(flags) {
return {};
}

flagsToFieldsPresent(30);

function isCadenceSupported(flags) {}

function dataviewToCyclingPowerMeasurement(dataview) {

// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// value: (0x) 30-00-21-00-2A-00-00-00-C4-60-12-00-F7-04
//

const flagsField = 0;
const instantaneousPowerField = 2;
const flagsFieldIndex = 0;
const instantaneousPowerFieldIndex = 2;

let flags = dataview.getUint16(flagsFieldIndex, true);
let power = dataview.getInt16(instantaneousPowerFieldIndex, true);

let fields = flagsToFieldsPresent(30);

let data = {
flags: dataview.getUint16(flagsField, true),
power: dataview.getInt16(instantaneousPowerField, true),
flags: flags,
power: power,
};

if(isCadenceSupported(flags)) {
const crankRevolutionsFieldIndex = 0;
let cadence = dataview.getInt16(crankRevolutionsFieldIndex, true);
data['cadence'] = cadence;
}

return data;
}

let cps = {
dataviewToCyclingPowerMeasurement: dataviewToCyclingPowerMeasurement,
dataviewToCyclingPowerMeasurement,
};

export { cps };
14 changes: 13 additions & 1 deletion css/flux.css
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ body.dark-theme {
position: relative;
height: 30px;
margin: 0px auto 20px;
color: (--gray);
color: var(--gray);
}
.file-btn-native {
position: relative;
Expand All @@ -877,6 +877,18 @@ body.dark-theme {
border: 1px solid var(--dark);
}
.file-btn-label {
display: inline-block;
height: 30px;
padding: 0 14px;
font-family: sans-serif;
font-size: 8px;
letter-spacing: 1px;
line-height: 29px;
text-transform: uppercase;
color: #fff;
background-color: var(--chrome);
border-radius: 3px;

position: absolute;
width: 120px;
z-index: 1;
Expand Down
1 change: 0 additions & 1 deletion images/icons/directions_bike-24px.svg

This file was deleted.

Binary file removed images/icons/ic_launcher_hdpi.png
Binary file not shown.
Binary file removed images/icons/ic_launcher_mdpi.png
Binary file not shown.
Binary file removed images/icons/ic_launcher_xhdpi.png
Binary file not shown.
Binary file removed images/icons/ic_launcher_xxhdpi.png
Binary file not shown.
Binary file removed images/icons/ic_launcher_xxxhdpi.png
Binary file not shown.
Binary file removed images/icons/playstore.png
Binary file not shown.
Binary file added images/logo/flux-logo-36dpi-192px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/logo/flux-logo-96dpi-512px-maskable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/logo/flux-logo-96dpi-512px.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
126 changes: 126 additions & 0 deletions images/logo/flux-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ <h2 class="t2">Workouts</h2>
<div class="workout-load-file">
<h2 class="h2">Load workout:</h2>
<div class="file-btn">
<label for="workout-file" class="file-btn-label btn">Select</label>
<label for="workout-file" class="file-btn-label">Select</label>
<input id="workout-file" class="file-btn-native" name="workout-file" type="file" value=""/>
</div>
</div>
Expand Down
9 changes: 9 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ import { DataMock } from './test/mock.js';

'use strict';

// if('serviceWorker' in navigator) {
// navigator.serviceWorker.register('./sw.js')
// .then(reg => {
// console.log(`SW: register success: ${reg}`);
// })
// .catch(err => {
// console.log(`SW: register error: ${err}`);
// });
// };

async function start() {
let hrb = new Hrb({name: 'hrb'});
Expand Down
Loading

0 comments on commit 37c285c

Please sign in to comment.