Skip to content

Commit

Permalink
Built as submitted to fitbit for app gallery
Browse files Browse the repository at this point in the history
Merge to master: Ded fnt clock
  • Loading branch information
tanstaaflFH authored Jul 26, 2018
2 parents 69ac373 + ab27fc1 commit 0825bab
Show file tree
Hide file tree
Showing 10 changed files with 335 additions and 407 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 Falko Hegewald

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
78 changes: 50 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
# DigiChrono
Clockface for Fitbit Versa

This clockface has two main views:

1. A simple and clean digital clock view showing
- the time in the middle (HH:MM)
- a slowly filling horizontal line denoting the seconds
- the date below
- a status bar on top:
+ LH corner: battery status
+ RH corner: heart rate

2. The current stats
- the time smaller on the upper area (HH:MM)
- a status bar on top:
+ LH corner: battery status
+ RH corner: heart rate
- the following stats, all shown in slowly filling arcs from

0 - the users goal
+ calories
+ active minutes
+ steps
+ floors
+ distance (in km)

You can switch between both views with a tap on the display
# DigiChron
**Clockface for Fitbit Versa**

## This clockface has two main views:

### 1. A simple and clean digital clock view showing
- the time in the middle (HH:MM)
- a slowly filling horizontal line denoting the seconds
- the date below
- a status bar on top:
- LH corner: battery status
- RH corner: heart rate

The time supports 24h and 12h format.
The day name supports DE (German) and EN (English) language. For all other, it defaults to english.
The date format supports US locale setting (M/D/YYYY), for all other locales, it defaults to D.M.YYYY

### 2. The current stats
- the time smaller on the upper area (HH:MM)
- a status bar on top:
- LH corner: battery status
- RH corner: heart rate
- the following stats, all shown in slowly filling arcs from zero to the users goal
- calories
- active minutes
- steps
- floors
- distance (in km)

If the goal is reached for a stat, a blur is surrounding the filled arc and a star is shown in the upper RH corner.

## Usage

You can switch between both views with a tap on the display

## Credits:

- The stars for completed goals are from [Freepik](https://www.freepik.com/free-vector/colorful-star-icons_787040.htm)

## Disclaimer:

The clockface was created as a hobby project.
I may or may not continue to work on it in the future.
Probably there will be some further version with some settings for color and with the support for more locales.

## Contact:

You can find the clockface sourcecode on [GitHub](https://github.com/tanstaaflFH/DigiChron)

You can contact me under [[email protected]](mailto://[email protected])
187 changes: 110 additions & 77 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,11 @@ import { HeartRateSensor } from "heart-rate";
import { battery } from "power";
import { today } from "user-activity";
import { goals } from "user-activity";
import { StatsObject } from "../common/WidgetStats"
import { batteryIcon } from "../common/WidgetStats"
import { StatsObject } from "../common/WidgetStats";
import { display } from "display";

var updateBattery = function () {

// update value
statBattery.setValue( Math.floor( battery.chargeLevel ) , false);
statBattery.setColorGradient ( 50 , false );

}

// get handler to the BG window
var bgWindow = document.getElementById("bgWindow");
// get handler to the click Target rectangle
var clickTarget = document.getElementById("clickTarget");
var bShowMainWindow = true;

// get the standard BG line width
Expand All @@ -32,20 +23,20 @@ var myHRZones = util.getHeartRateZones();

// initialize all objects for the stat elements
if ( !myHRZones ) {
var statHeartRate = new StatsObject("icnHR", "txtHeartRate", "lnHR", "lnHRBG", 50, 180, false, 0 );
var statHeartRate = new StatsObject("HeartRate" , 50, 180, false, 0 );
} else {
var statHeartRate = new StatsObject("icnHR", "txtHeartRate", "lnHR", "lnHRBG", myHRZones[0][0], myHRZones[0][2], false, 0 );
var statHeartRate = new StatsObject("HeartRate", myHRZones[0][0], myHRZones[0][2], false, 0 );
}
var statBattery = new StatsObject("icnBattery", "txtBattery", "lnBattery", "lnBatteryBG", 0, 100, true, 0 );
var statBattery = new StatsObject("Battery", 0, 100, true, 0 );
statBattery.suffix = "%";
var statSteps = new StatsObject("icnSteps", "txtSteps", "lnSteps", "lnStepsBG", 0, goals.steps || 0, true, 1 );
var statStairs = new StatsObject("icnFloors", "txtFloors", "lnFloors", "lnFloorsBG", 0, goals.elevationGain || 0, true, 1 );
var statCalories = new StatsObject("icnCalories", "txtCalories", "lnCalories", "lnCaloriesBG", 0, goals.calories || 0, true, 1 );
var statDistance = new StatsObject("icnDistance", "txtDistance", "lnDistance", "lnDistanceBG", 0, goals.distance || 0, true, 1 );
var statSteps = new StatsObject("Steps", 0, goals.steps || 0, true, 1 );
var statStairs = new StatsObject("Floors", 0, goals.elevationGain || 0, true, 1 );
var statCalories = new StatsObject("Calories", 0, goals.calories || 0, true, 1 );
var statDistance = new StatsObject("Distance", 0, goals.distance || 0, true, 1 );
statDistance.decimal = 1;
statDistance.factor = 1/1000;
statDistance.suffix = "k";
var statActive = new StatsObject("icnActiveMinutes", "txtActiveMinutes", "lnActiveMinutes", "lnActiveMinutesBG", 0, goals.activeMinutes || 0, true, 1 );
var statActive = new StatsObject("ActiveMinutes", 0, goals.activeMinutes || 0, true, 1 );

// Clock Elements
var myClock = document.getElementById("txtHourMin");
Expand All @@ -63,64 +54,36 @@ var hrm = new HeartRateSensor();
// Update the clock every second
clock.granularity = "seconds";
// Begin monitoring the heart rate sensor
hrm.start();
hrm.start();
// initialize the battery
updateBattery();

// Update the UI elements every tick
clock.ontick = (evt) => {

// only if display on
if ( !display.on ) { return; }
// update the clock elements
updateClock(evt.date);

// Block variables --> now, hours, minutes (needed independently of which window is shown)
let now = evt.date;
let hours = now.getHours();
let mins = util.zeroPad(now.getMinutes());
let seconds = now.getSeconds();
// update stat elements if 2nd window is shown
if ( !bShowMainWindow ) {

// 12h or 24h format for hours (needed independently of which window is shown)
if (preferences.clockDisplay === "12h") {
// 12h format
hours = hours % 12 || 12;
} else {
// 24h format
hours = util.zeroPad(hours);
}

// update clock, date and stat elements depending on which window is shown
if ( bShowMainWindow ) {
// get seconds value
let seconds = evt.date.getSeconds();

// * Primary Window *
// define seconds FG line
let lnSecWidth = seconds * lnSecBGWidth / 60;
let lnSecX = lnSecBGX + ( lnSecBGWidth / 2 ) - ( lnSecWidth / 2 );

// output Time and Date
myClock.text = `${hours}:${mins}`;
myDate.text = localeUtil.getDateStringLocale( now, true );
myLnSec.width = lnSecWidth;
myLnSec.x = lnSecX;

} else {

// output Time
myClockSmall.text = `${hours}:${mins}`;

// update stats each second (only steps)
statSteps.setValue( today.local.steps, true );
statSteps.setValue( today.adjusted.steps, true );

// update rest each 3 seconds
if ( ( seconds % 3 ) == 0 ) {
statStairs.setValue( today.local.elevationGain, true );
statCalories.setValue( today.local.calories, true );
statActive.setValue( today.local.activeMinutes, true );
statDistance.setValue( today.local.distance, true );
if ( ( seconds % 3 ) === 0 ) {
statStairs.setValue( today.adjusted.elevationGain, true );
statCalories.setValue( today.adjusted.calories, true );
statActive.setValue( today.adjusted.activeMinutes, true );
statDistance.setValue( today.adjusted.distance, true );
}

}

}
};

// read HR
hrm.onreading = function() {
Expand All @@ -136,7 +99,7 @@ hrm.onreading = function() {
statHeartRate.setColorGradientIcon( myHRZones[0][1] , true );
}

}
};

// read Battery
battery.onchange = function() {
Expand All @@ -146,26 +109,44 @@ battery.onchange = function() {

updateBattery();

}
};

// click event for the Background Window
bgWindow.onclick = function() {
// click event for the; Background Window
clickTarget.onclick = function() {

// toggle global variable showing which elements shall be shown
bShowMainWindow = !bShowMainWindow;

// hide show the elements accordingly
showElements ( bShowMainWindow );

}
};

// event called when the display is switched on or off
display.onchange = function() {

if ( display.on ) { hrm.start(); } else { hrm.stop(); }
if ( display.on ) {

// start the heart rate monitor
hrm.start();

// update the clock elements
let now = new Date();
updateClock(now);

} else {

// stop the heart rate monitor for battery saving
hrm.stop();

}

}
};

var showElements = function ( isMainWindow ) {
function showElements( isMainWindow ) {

// get current time
let now = new Date();

// show or hide the stat and clock elements according to the set state
if ( isMainWindow === true ) {
Expand All @@ -177,7 +158,8 @@ var showElements = function ( isMainWindow ) {
statActive.hide();
statDistance.hide();

// clock elements
// clock elements, update before
updateClock(now);
myClock.style.display = "inline";
myDate.style.display = "inline";
myLnSec.style.display = "inline";
Expand All @@ -187,11 +169,11 @@ var showElements = function ( isMainWindow ) {
} else {

// update stats
statCalories.setValue( today.local.calories, true );
statStairs.setValue( today.local.elevationGain, true );
statSteps.setValue( today.local.steps, true );
statActive.setValue( today.local.activeMinutes, true );
statDistance.setValue( today.local.distance, true );
statCalories.setValue( today.adjusted.calories, true );
statStairs.setValue( today.adjusted.elevationGain, true );
statSteps.setValue( today.adjusted.steps, true );
statActive.setValue( today.adjusted.activeMinutes, true );
statDistance.setValue( today.adjusted.distance, true );

// stat elements
statCalories.show();
Expand All @@ -200,7 +182,8 @@ var showElements = function ( isMainWindow ) {
statActive.show();
statDistance.show();

// clock elements
// clock elements, update before
updateClock(now);
myClock.style.display = "none";
myDate.style.display = "none";
myLnSec.style.display = "none";
Expand All @@ -213,3 +196,53 @@ var showElements = function ( isMainWindow ) {
updateBattery();

}

// update the battery elements
function updateBattery() {

// update value
statBattery.setValue( Math.floor( battery.chargeLevel ) , false);
statBattery.setColorGradient ( 50 , false );

}

// update the clock elements
function updateClock( inpDate ) {

// Block variables --> now, hours, minutes (needed independently of which window is shown)
let hours = inpDate.getHours();
let mins = util.zeroPad(inpDate.getMinutes());
let seconds = inpDate.getSeconds();

// 12h or 24h format for hours (needed independently of which window is shown)
if (preferences.clockDisplay === "12h") {
// 12h format
hours = hours % 12 || 12;
} else {
// 24h format
hours = util.zeroPad(hours);
}

// update clock, date and stat elements depending on which window is shown
if ( bShowMainWindow ) {

// * Primary Window *
// define seconds FG line
let lnSecWidth = seconds * lnSecBGWidth / 60;
let lnSecX = lnSecBGX + ( lnSecBGWidth / 2 ) - ( lnSecWidth / 2 );

// output Time and Date
myClock.text = `${hours}:${mins}`;
myDate.text = localeUtil.getDateStringLocale( inpDate, true );
myLnSec.width = lnSecWidth;
myLnSec.x = lnSecX;

} else {

// output Time
myClockSmall.text = `${hours}:${mins}`;

}

};

Loading

0 comments on commit 0825bab

Please sign in to comment.