├── website
├── src
│ ├── boot
│ │ ├── .gitkeep
│ │ ├── axios.js
│ │ └── i18n.js
│ ├── components
│ │ ├── .gitkeep
│ │ ├── GlanceFooter.vue
│ │ ├── Gallery.vue
│ │ ├── GlanceHeader.vue
│ │ ├── Navbar.vue
│ │ ├── Donations.vue
│ │ └── Features.vue
│ ├── store
│ │ ├── module-example
│ │ │ ├── state.js
│ │ │ ├── actions.js
│ │ │ ├── getters.js
│ │ │ ├── mutations.js
│ │ │ └── index.js
│ │ └── index.js
│ ├── i18n
│ │ ├── index.js
│ │ └── en-us
│ │ │ └── index.js
│ ├── App.vue
│ ├── css
│ │ ├── app.styl
│ │ └── quasar.variables.styl
│ ├── pages
│ │ ├── Error404.vue
│ │ ├── Index.vue
│ │ ├── Troubleshooting.vue
│ │ └── Customize.vue
│ ├── router
│ │ ├── routes.js
│ │ └── index.js
│ ├── index.template.html
│ ├── layouts
│ │ └── MyLayout.vue
│ └── assets
│ │ ├── sad.svg
│ │ └── quasar-logo-full.svg
├── .eslintignore
├── README.md
├── public
│ └── statics
│ │ ├── bg.jpg
│ │ ├── bg2.jpg
│ │ ├── bg3.jpg
│ │ ├── bg4.jpg
│ │ ├── logo.png
│ │ ├── favicon.png
│ │ ├── gallery.png
│ │ ├── header.jpg
│ │ ├── morocco.png
│ │ ├── tomato1.jpg
│ │ ├── tomato2.jpg
│ │ ├── gallery10.png
│ │ ├── gallery11.jpg
│ │ ├── gallery2.png
│ │ ├── gallery3.png
│ │ ├── gallery4.jpg
│ │ ├── gallery5.png
│ │ ├── gallery6.png
│ │ ├── gallery7.jpg
│ │ ├── gallery8.jpg
│ │ ├── gallery8.png
│ │ ├── gallery9.png
│ │ ├── dark-paths.png
│ │ ├── decisionTree.png
│ │ ├── paypal-logo.png
│ │ ├── icons
│ │ ├── favicon.ico
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── favicon-96x96.png
│ │ ├── icon-128x128.png
│ │ ├── icon-192x192.png
│ │ ├── icon-256x256.png
│ │ ├── icon-384x384.png
│ │ ├── icon-512x512.png
│ │ ├── ms-icon-144x144.png
│ │ ├── apple-icon-120x120.png
│ │ ├── apple-icon-152x152.png
│ │ ├── apple-icon-167x167.png
│ │ ├── apple-icon-180x180.png
│ │ └── safari-pinned-tab.svg
│ │ ├── Gallery
│ │ ├── gallery2.png
│ │ ├── gallery3.png
│ │ ├── gallery4.png
│ │ └── gallery5.png
│ │ └── app-logo-128x128.png
├── .editorconfig
├── .postcssrc.js
├── babel.config.cjs
├── .gitignore
├── .stylintrc
├── package.json
├── .eslintrc.cjs
└── quasar.conf.js
├── resources
├── img
│ ├── add.png
│ ├── degree.png
│ ├── heart.png
│ ├── home.png
│ ├── step.png
│ ├── battery.png
│ ├── checked.png
│ ├── syringe.png
│ ├── arrows
│ │ ├── Flat.png
│ │ ├── None.png
│ │ ├── DoubleUp.png
│ │ ├── SingleUp.png
│ │ ├── loading.png
│ │ ├── warning.png
│ │ ├── DoubleDown.png
│ │ ├── FortyFiveUp.png
│ │ ├── SingleDown.png
│ │ └── FortyFiveDown.png
│ └── hamburger.png
├── widget.defs
├── styles.css
├── styles~300x300.css
├── styles~336x336.css
└── index.view
├── .gitignore
├── .pre-commit-config.yaml
├── .vscode
└── tasks.json
├── modules
├── app
│ ├── sharedPreferences.js
│ ├── treatments.js
│ ├── userActivity.js
│ ├── batteryLevels.js
│ ├── errors.js
│ ├── transfer.js
│ ├── dateTime.js
│ ├── alerts.js
│ └── bloodline.js
└── companion
│ ├── transfer.js
│ ├── weather.js
│ ├── logs.js
│ ├── sizeof.js
│ ├── fetch.js
│ └── dexcom.js
├── .github
├── dependabot.yml
└── workflows
│ ├── dependency-review.yml
│ ├── codeql.yml
│ └── scorecards.yml
├── package.json
├── README.md
├── companion
└── index.js
├── app
└── index.js
└── settings
└── index.jsx
/website/src/boot/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/website/.eslintignore:
--------------------------------------------------------------------------------
1 | /dist
2 |
--------------------------------------------------------------------------------
/website/src/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # Quasar App
2 |
3 | > WIP
4 |
--------------------------------------------------------------------------------
/website/src/store/module-example/state.js:
--------------------------------------------------------------------------------
1 | export default {
2 | //
3 | };
4 |
--------------------------------------------------------------------------------
/resources/img/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/add.png
--------------------------------------------------------------------------------
/website/src/store/module-example/actions.js:
--------------------------------------------------------------------------------
1 | export function someAction(/* context */) {}
2 |
--------------------------------------------------------------------------------
/website/src/store/module-example/getters.js:
--------------------------------------------------------------------------------
1 | export function someGetter(/* state */) {}
2 |
--------------------------------------------------------------------------------
/resources/img/degree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/degree.png
--------------------------------------------------------------------------------
/resources/img/heart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/heart.png
--------------------------------------------------------------------------------
/resources/img/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/home.png
--------------------------------------------------------------------------------
/resources/img/step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/step.png
--------------------------------------------------------------------------------
/website/src/store/module-example/mutations.js:
--------------------------------------------------------------------------------
1 | export function someMutation(/* state */) {}
2 |
--------------------------------------------------------------------------------
/resources/img/battery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/battery.png
--------------------------------------------------------------------------------
/resources/img/checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/checked.png
--------------------------------------------------------------------------------
/resources/img/syringe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/syringe.png
--------------------------------------------------------------------------------
/resources/img/arrows/Flat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/Flat.png
--------------------------------------------------------------------------------
/resources/img/arrows/None.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/None.png
--------------------------------------------------------------------------------
/resources/img/hamburger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/hamburger.png
--------------------------------------------------------------------------------
/website/public/statics/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/bg.jpg
--------------------------------------------------------------------------------
/website/public/statics/bg2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/bg2.jpg
--------------------------------------------------------------------------------
/website/public/statics/bg3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/bg3.jpg
--------------------------------------------------------------------------------
/website/public/statics/bg4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/bg4.jpg
--------------------------------------------------------------------------------
/website/public/statics/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/logo.png
--------------------------------------------------------------------------------
/website/src/i18n/index.js:
--------------------------------------------------------------------------------
1 | import enUS from "./en-us";
2 |
3 | export default {
4 | "en-us": enUS
5 | };
6 |
--------------------------------------------------------------------------------
/resources/img/arrows/DoubleUp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/DoubleUp.png
--------------------------------------------------------------------------------
/resources/img/arrows/SingleUp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/SingleUp.png
--------------------------------------------------------------------------------
/resources/img/arrows/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/loading.png
--------------------------------------------------------------------------------
/resources/img/arrows/warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/warning.png
--------------------------------------------------------------------------------
/website/public/statics/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/favicon.png
--------------------------------------------------------------------------------
/website/public/statics/gallery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery.png
--------------------------------------------------------------------------------
/website/public/statics/header.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/header.jpg
--------------------------------------------------------------------------------
/website/public/statics/morocco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/morocco.png
--------------------------------------------------------------------------------
/website/public/statics/tomato1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/tomato1.jpg
--------------------------------------------------------------------------------
/website/public/statics/tomato2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/tomato2.jpg
--------------------------------------------------------------------------------
/resources/img/arrows/DoubleDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/DoubleDown.png
--------------------------------------------------------------------------------
/resources/img/arrows/FortyFiveUp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/FortyFiveUp.png
--------------------------------------------------------------------------------
/resources/img/arrows/SingleDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/SingleDown.png
--------------------------------------------------------------------------------
/website/public/statics/gallery10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery10.png
--------------------------------------------------------------------------------
/website/public/statics/gallery11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery11.jpg
--------------------------------------------------------------------------------
/website/public/statics/gallery2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery2.png
--------------------------------------------------------------------------------
/website/public/statics/gallery3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery3.png
--------------------------------------------------------------------------------
/website/public/statics/gallery4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery4.jpg
--------------------------------------------------------------------------------
/website/public/statics/gallery5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery5.png
--------------------------------------------------------------------------------
/website/public/statics/gallery6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery6.png
--------------------------------------------------------------------------------
/website/public/statics/gallery7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery7.jpg
--------------------------------------------------------------------------------
/website/public/statics/gallery8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery8.jpg
--------------------------------------------------------------------------------
/website/public/statics/gallery8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery8.png
--------------------------------------------------------------------------------
/website/public/statics/gallery9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/gallery9.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependency directories
2 | node_modules
3 | /node_modules
4 |
5 | # build folder
6 | build
7 | /build
8 |
9 |
--------------------------------------------------------------------------------
/resources/img/arrows/FortyFiveDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/resources/img/arrows/FortyFiveDown.png
--------------------------------------------------------------------------------
/website/public/statics/dark-paths.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/dark-paths.png
--------------------------------------------------------------------------------
/website/public/statics/decisionTree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/decisionTree.png
--------------------------------------------------------------------------------
/website/public/statics/paypal-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/paypal-logo.png
--------------------------------------------------------------------------------
/website/public/statics/icons/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/favicon.ico
--------------------------------------------------------------------------------
/website/public/statics/Gallery/gallery2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/Gallery/gallery2.png
--------------------------------------------------------------------------------
/website/public/statics/Gallery/gallery3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/Gallery/gallery3.png
--------------------------------------------------------------------------------
/website/public/statics/Gallery/gallery4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/Gallery/gallery4.png
--------------------------------------------------------------------------------
/website/public/statics/Gallery/gallery5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/Gallery/gallery5.png
--------------------------------------------------------------------------------
/website/public/statics/app-logo-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/app-logo-128x128.png
--------------------------------------------------------------------------------
/website/src/boot/axios.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default async ({ app, Vue }) => {
4 | app.$axios = axios
5 | };
6 |
--------------------------------------------------------------------------------
/website/public/statics/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/website/public/statics/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/website/public/statics/icons/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/favicon-96x96.png
--------------------------------------------------------------------------------
/website/public/statics/icons/icon-128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/icon-128x128.png
--------------------------------------------------------------------------------
/website/public/statics/icons/icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/icon-192x192.png
--------------------------------------------------------------------------------
/website/public/statics/icons/icon-256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/icon-256x256.png
--------------------------------------------------------------------------------
/website/public/statics/icons/icon-384x384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/icon-384x384.png
--------------------------------------------------------------------------------
/website/public/statics/icons/icon-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/icon-512x512.png
--------------------------------------------------------------------------------
/website/public/statics/icons/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/ms-icon-144x144.png
--------------------------------------------------------------------------------
/website/public/statics/icons/apple-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/apple-icon-120x120.png
--------------------------------------------------------------------------------
/website/public/statics/icons/apple-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/apple-icon-152x152.png
--------------------------------------------------------------------------------
/website/public/statics/icons/apple-icon-167x167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/apple-icon-167x167.png
--------------------------------------------------------------------------------
/website/public/statics/icons/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rytiggy/Glance/HEAD/website/public/statics/icons/apple-icon-180x180.png
--------------------------------------------------------------------------------
/website/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/website/src/i18n/en-us/index.js:
--------------------------------------------------------------------------------
1 | // This is just an example,
2 | // so you can safely delete all default props below
3 |
4 | export default {
5 | failed: "Action failed",
6 | success: "Action was successful"
7 | };
8 |
--------------------------------------------------------------------------------
/website/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
--------------------------------------------------------------------------------
/website/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | plugins: [
5 | // to edit target browsers: use "browserslist" field in package.json
6 | require('autoprefixer')
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/website/src/store/module-example/index.js:
--------------------------------------------------------------------------------
1 | import state from "./state";
2 | import * as getters from "./getters";
3 | import * as mutations from "./mutations";
4 | import * as actions from "./actions";
5 |
6 | export default {
7 | namespaced: true,
8 | getters,
9 | mutations,
10 | actions,
11 | state
12 | };
13 |
--------------------------------------------------------------------------------
/website/src/boot/i18n.js:
--------------------------------------------------------------------------------
1 | import { createI18n } from 'vue-i18n'
2 | import messages from "src/i18n";
3 |
4 | export default async ({ app, Vue }) => {
5 | const i18n = createI18n({
6 | locale: "en-us",
7 | fallbackLocale: "en-us",
8 | messages
9 | });
10 |
11 | // Set i18n instance on app
12 | app.use(i18n);
13 | };
14 |
--------------------------------------------------------------------------------
/website/babel.config.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | module.exports = api => {
4 | return {
5 | presets: [
6 | [
7 | '@quasar/babel-preset-app',
8 | api.caller(caller => caller && caller.target === 'node')
9 | ? { targets: { node: 'current' } }
10 | : {}
11 | ]
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | .quasar
2 | .DS_Store
3 | .thumbs.db
4 | node_modules
5 | /dist
6 | /src-cordova/node_modules
7 | /src-cordova/platforms
8 | /src-cordova/plugins
9 | /src-cordova/www
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/gitleaks/gitleaks
3 | rev: v8.16.3
4 | hooks:
5 | - id: gitleaks
6 | - repo: https://github.com/pre-commit/mirrors-eslint
7 | rev: v8.38.0
8 | hooks:
9 | - id: eslint
10 | - repo: https://github.com/pre-commit/pre-commit-hooks
11 | rev: v4.4.0
12 | hooks:
13 | - id: end-of-file-fixer
14 | - id: trailing-whitespace
15 |
--------------------------------------------------------------------------------
/website/src/css/app.styl:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Faster+One&display=swap');
2 | .glance-logo {
3 | font-family: 'Faster One', cursive;
4 | }
5 | body {
6 | min-width: 320px;
7 | }
8 | .description-lead {
9 | max-width: 600px;
10 | margin: auto;
11 | }
12 | .bullet {
13 | display: list-item;
14 | list-style-type: disc;
15 | list-style-position: inside;
16 | }
17 |
18 | .no-underline:hover {
19 | text-decoration: none;
20 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "type": "npm",
8 | "script": "build",
9 | "problemMatcher": []
10 | },
11 | {
12 | "type": "npm",
13 | "script": "build-and-install",
14 | "problemMatcher": []
15 | },
16 | {
17 | "type": "npm",
18 | "script": "install",
19 | "problemMatcher": []
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/website/src/pages/Error404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Sorry, nothing here...(404)
7 |
Go back
10 |
11 |
12 |
13 |
18 |
--------------------------------------------------------------------------------
/website/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuex from "vuex";
3 |
4 | // import example from './module-example'
5 |
6 | Vue.use(Vuex);
7 |
8 | /*
9 | * If not building with SSR mode, you can
10 | * directly export the Store instantiation
11 | */
12 |
13 | export default function(/* { ssrContext } */) {
14 | const Store = new Vuex.Store({
15 | modules: {
16 | // example
17 | },
18 |
19 | // enable strict mode (adds overhead!)
20 | // for dev mode only
21 | strict: process.env.DEV
22 | });
23 |
24 | return Store;
25 | }
26 |
--------------------------------------------------------------------------------
/modules/app/sharedPreferences.js:
--------------------------------------------------------------------------------
1 | import { readFileSync, writeFileSync } from 'fs';
2 | import { me } from 'appbit';
3 |
4 | const FILE_NAME = 'datamatabatafata.cbor';
5 |
6 | export let preferences = {};
7 |
8 | try {
9 | preferences = readFileSync(FILE_NAME, 'cbor');
10 | } catch (error) {
11 | console.warn('Failed to load ' + FILE_NAME + '. It is OK if no values were stored yet.');
12 | }
13 |
14 | me.addEventListener('unload', () => {
15 | try {
16 | writeFileSync(FILE_NAME, preferences, 'cbor');
17 | } catch (error) {
18 | console.error('Failed to save ' + FILE_NAME);
19 | }
20 | })
21 |
--------------------------------------------------------------------------------
/website/src/router/routes.js:
--------------------------------------------------------------------------------
1 | const routes = [
2 | {
3 | path: '/',
4 | component: () => import('layouts/MyLayout.vue'),
5 | children: [
6 | { path: '', component: () => import('pages/Index.vue') },
7 | {
8 | path: "/troubleshooting",
9 | component: () => import("pages/Troubleshooting.vue")
10 | },
11 | { path: "/customize", component: () => import("pages/Customize.vue") }
12 | ]
13 | },
14 |
15 | // Always leave this as last one,
16 | // but you can also remove it
17 | {
18 | path: '/:catchAll(.*)*',
19 | component: () => import('pages/Error404.vue')
20 | }
21 | ]
22 |
23 | export default routes
24 |
--------------------------------------------------------------------------------
/website/src/css/quasar.variables.styl:
--------------------------------------------------------------------------------
1 | // Quasar Stylus Variables
2 | // --------------------------------------------------
3 | // To customize the look and feel of this app, you can override
4 | // the Stylus variables found in Quasar's source Stylus files.
5 |
6 | // Check documentation for full list of Quasar variables
7 |
8 | // It's highly recommended to change the default colors
9 | // to match your app's branding.
10 | // Tip: Use the "Theme Builder" on Quasar's documentation website.
11 |
12 | $primary = rgb(75, 162, 220);
13 | $secondary = rgba(34, 34, 34, 0.9);
14 | $accent = #9C27B0
15 |
16 | $positive = #21BA45
17 | $negative = #C10015
18 | $info = #31CCEC
19 | $warning = gold
20 |
21 | $facebook = #3b5998
22 |
--------------------------------------------------------------------------------
/website/src/index.template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= productName %>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "npm"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
7 | groups:
8 | core:
9 | update-types:
10 | - "major"
11 | - "minor"
12 | - "patch"
13 | - package-ecosystem: "npm"
14 | directory: "/website"
15 | schedule:
16 | interval: "weekly"
17 | groups:
18 | website:
19 | update-types:
20 | - "major"
21 | - "minor"
22 | - "patch"
23 | - package-ecosystem: "github-actions"
24 | directory: "/"
25 | schedule:
26 | interval: "weekly"
27 | groups:
28 | github-actions:
29 | update-types:
30 | - "major"
31 | - "minor"
32 | - "patch"
33 |
--------------------------------------------------------------------------------
/resources/widget.defs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "fitbit": {
3 | "appUUID": "7b5d9822-7e8e-41f9-a2a7-e823548c001c",
4 | "appType": "clockface",
5 | "appDisplayName": "Glance",
6 | "iconFile": "resources/icon.png",
7 | "wipeColor": "#673ab7",
8 | "requestedPermissions": [
9 | "access_user_profile",
10 | "access_location",
11 | "access_internet",
12 | "run_background",
13 | "access_activity",
14 | "access_heart_rate"
15 | ],
16 | "buildTargets": [
17 | "vulcan",
18 | "atlas"
19 | ],
20 | "i18n": {
21 | "en": {
22 | "name": "Glance 2.0"
23 | }
24 | }
25 | },
26 | "devDependencies": {
27 | "@fitbit/sdk": "~5.0.1",
28 | "@fitbit/sdk-cli": "^1.7.3"
29 | },
30 | "scripts": {
31 | "build": "fitbit-build",
32 | "build-and-install": "printf 'build-and-install' | npx fitbit",
33 | "install": "printf 'install' | npx fitbit"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/website/src/pages/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
37 |
46 |
--------------------------------------------------------------------------------
/website/.stylintrc:
--------------------------------------------------------------------------------
1 | {
2 | "blocks": "never",
3 | "brackets": "never",
4 | "colons": "never",
5 | "colors": "always",
6 | "commaSpace": "always",
7 | "commentSpace": "always",
8 | "cssLiteral": "never",
9 | "depthLimit": false,
10 | "duplicates": true,
11 | "efficient": "always",
12 | "extendPref": false,
13 | "globalDupe": true,
14 | "indentPref": 2,
15 | "leadingZero": "never",
16 | "maxErrors": false,
17 | "maxWarnings": false,
18 | "mixed": false,
19 | "namingConvention": false,
20 | "namingConventionStrict": false,
21 | "none": "never",
22 | "noImportant": false,
23 | "parenSpace": "never",
24 | "placeholder": false,
25 | "prefixVarsWithDollar": "always",
26 | "quotePref": "single",
27 | "semicolons": "never",
28 | "sortOrder": false,
29 | "stackedProperties": "never",
30 | "trailingWhitespace": "never",
31 | "universal": "never",
32 | "valid": true,
33 | "zeroUnits": "never",
34 | "zIndexNormalize": false
35 | }
36 |
--------------------------------------------------------------------------------
/modules/app/treatments.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | import document from "document";
17 | let enterTreatment = document.getElementById("enterTreatment");
18 |
19 | export default class treatments {
20 | check() {
21 | console.log('app - treatments - check()')
22 |
23 | }
24 |
25 | };
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/modules/app/userActivity.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | import { today } from 'user-activity';
17 | import { HeartRateSensor } from "heart-rate";
18 |
19 | let hrm = new HeartRateSensor();
20 | hrm.start();
21 |
22 | export default class userActivity {
23 | get() {
24 | const userActivity = {
25 | steps: today.adjusted.steps,
26 | heartRate: hrm.heartRate,
27 | }
28 | return userActivity;
29 | }
30 | };
--------------------------------------------------------------------------------
/website/src/router/index.js:
--------------------------------------------------------------------------------
1 | import { route } from 'quasar/wrappers'
2 | import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
3 | import routes from './routes'
4 |
5 | /*
6 | * If not building with SSR mode, you can
7 | * directly export the Router instantiation;
8 | *
9 | * The function below can be async too; either use
10 | * async/await or return a Promise which resolves
11 | * with the Router instance.
12 | */
13 |
14 | export default route(function (/* { store, ssrContext } */) {
15 | const createHistory = process.env.SERVER
16 | ? createMemoryHistory
17 | : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
18 |
19 | const Router = createRouter({
20 | scrollBehavior: () => ({ left: 0, top: 0 }),
21 | routes,
22 |
23 | // Leave this as is and make changes in quasar.conf.js instead!
24 | // quasar.conf.js -> build -> vueRouterMode
25 | // quasar.conf.js -> build -> publicPath
26 | history: createHistory(process.env.VUE_ROUTER_BASE)
27 | })
28 |
29 | return Router
30 | })
31 |
--------------------------------------------------------------------------------
/.github/workflows/dependency-review.yml:
--------------------------------------------------------------------------------
1 | # Dependency Review Action
2 | #
3 | # This Action will scan dependency manifest files that change as part of a Pull Request,
4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR.
5 | # Once installed, if the workflow run is marked as required,
6 | # PRs introducing known-vulnerable packages will be blocked from merging.
7 | #
8 | # Source repository: https://github.com/actions/dependency-review-action
9 | name: 'Dependency Review'
10 | on: [pull_request]
11 |
12 | permissions:
13 | contents: read
14 |
15 | jobs:
16 | dependency-review:
17 | runs-on: ubuntu-latest
18 | steps:
19 | - name: Harden Runner
20 | uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
21 | with:
22 | egress-policy: audit
23 |
24 | - name: 'Checkout Repository'
25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
26 | - name: 'Dependency Review'
27 | uses: actions/dependency-review-action@4081bf99e2866ebe428fc0477b69eb4fcda7220a # v4.4.0
28 |
--------------------------------------------------------------------------------
/modules/companion/transfer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | import Logs from "./logs.js";
17 | const logs = new Logs();
18 | // This module handles all messaging protocols
19 | import { outbox } from "file-transfer";
20 | import { encode } from 'cbor';
21 | import * as messaging from "messaging";
22 |
23 | export default class transfer {
24 | // Send data to the watchface
25 | send(data) {
26 | logs.add('Line 19: companion - transfer - send()')
27 | outbox.enqueue("responce2.json", encode(data));
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/modules/app/batteryLevels.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 |
17 | import { charger, battery } from "power";
18 |
19 | export default class batteryLevels {
20 | get() {
21 | console.log('app - batteryLevels - get()')
22 | let percent = Math.floor(battery.chargeLevel)
23 | let level = .3 * percent;
24 | let color = '#75bd78';
25 | if(percent <= 30 && percent >= 15) {
26 | color = 'orange';
27 | } else if( percent <= 15) {
28 | color = 'red';
29 | }
30 | return {
31 | percent: percent,
32 | level: level,
33 | color: color,
34 | }
35 | }
36 | };
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "glance",
3 | "version": "0.0.1",
4 | "description": "Glance is a application for use with Fitbit devices to view your blood glucose levels along with a variety of other health stats on the watch face. You can see your stats at a glance!",
5 | "productName": "Glance Watchface",
6 | "cordovaId": "org.cordova.quasar.app",
7 | "author": "Ryan Mason ",
8 | "private": true,
9 | "scripts": {
10 | "lint": "eslint --ext .js,.vue src",
11 | "test": "echo \"No test specified\" && exit 0"
12 | },
13 | "dependencies": {
14 | "@quasar/extras": "^1.16.12",
15 | "axios": "^1.7.7",
16 | "quasar": "^2.17.1",
17 | "stylus-loader": "^8.1.1",
18 | "vue": "^3.5.12",
19 | "vue-i18n": "^10.0.4"
20 | },
21 | "devDependencies": {
22 | "@babel/eslint-parser": "^7.25.9",
23 | "@quasar/app-webpack": "^3.14.2",
24 | "eslint": "^9.14.0",
25 | "eslint-config-prettier": "^9.1.0",
26 | "eslint-plugin-vue": "^9.30.0",
27 | "eslint-webpack-plugin": "^4.2.0",
28 | "prettier": "^3.3.3"
29 | },
30 | "engines": {
31 | "node": ">= 18.12.0",
32 | "npm": ">= 5.6.0",
33 | "yarn": ">= 1.6.0"
34 | },
35 | "browserslist": [
36 | "last 1 version, not dead, ie >= 11"
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/modules/companion/weather.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | import { geolocation } from "geolocation";
17 |
18 | export default class weather {
19 | async get(tempType) {
20 | console.log('companion - weather - get()')
21 | const position = await getCurrentPosition();
22 | const { latitude, longitude } = position.coords;
23 | let query = 'select item.condition from weather.forecast where woeid in (SELECT woeid FROM geo.places WHERE text="('+ latitude + "," +longitude+')") and u="'+tempType+'"';
24 | let endPointURL = "https://query.yahooapis.com/v1/public/yql?q=" +escape(query) + "&format=json";
25 | return endPointURL;
26 | };
27 | };
28 |
29 |
30 | function getCurrentPosition(options = {}) {
31 | return new Promise((resolve, reject) => {
32 | geolocation.getCurrentPosition(resolve, reject, options);
33 | });
34 | };
--------------------------------------------------------------------------------
/modules/app/errors.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | import document from "document";
17 | let errorLine = document.getElementById("errorLine");
18 | let largeGraphErrorLine = document.getElementById("largeGraphErrorLine");
19 |
20 | let sgv = document.getElementById("sgv");
21 | let largeGraphsSgv = document.getElementById("largeGraphsSgv");
22 |
23 | export default class errors {
24 | check(timeSenseLastSGV) {
25 | console.log('app - errors - check()')
26 | // if the bloodsugar is stale
27 | if (parseInt(timeSenseLastSGV, 10) >= 15) {
28 | errorLine.style.display = "inline";
29 | largeGraphErrorLine.style.display = "inline";
30 | errorLine.style.fill = 'gray';
31 | largeGraphErrorLine.style.fill = 'gray'
32 | sgv.style.fill = 'gray';
33 | largeGraphsSgv.style.fill = 'gray'
34 | } else {
35 | errorLine.style.display = "none";
36 | largeGraphErrorLine.style.display = "none";
37 | }
38 | }
39 | };
40 |
41 |
--------------------------------------------------------------------------------
/modules/companion/logs.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | import { settingsStorage } from "settings";
17 | import Sizeof from "./sizeof.js";
18 |
19 | const sizeof = new Sizeof();
20 |
21 | export default class logs {
22 | add(value) {
23 | console.log(value)
24 | let d = new Date();
25 | // console.error(sizeof.size(settingsStorage.getItem('logs')))
26 | if (settingsStorage.getItem('logs') && sizeof.size(settingsStorage.getItem('logs')) > 130000) {
27 | settingsStorage.setItem('logs', JSON.stringify({"name":''}));
28 | }
29 |
30 | if( settingsStorage.getItem('logs') && JSON.parse(settingsStorage.getItem('logs')).name ) {
31 | settingsStorage.setItem('logs', JSON.stringify({"name":( `${ d.getHours() } : ${ d.getMinutes() } ${ value } |,| ${JSON.parse(settingsStorage.getItem('logs')).name}`)}));
32 | } else {// if there are no logs
33 | settingsStorage.setItem('logs', JSON.stringify({'name':( `${ d.getHours() } : ${ d.getMinutes() } ${ value } `)}));
34 | }
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | # Glance 
11 |
12 | Glance is an application for use with Fitbit devices to view your blood glucose levels along with a variety of other health stats on the watch face. You can see your stats at a glance!
13 | Click here to learn how to set up Glance!
14 |
15 |
16 |
17 | ## Donation
18 | I developed Glance to help people with diabetes! 50% of all donations will go to the Faustman lab . The remaining 50% will be dedicated to future research and development of Glance.
19 |
20 | [](https://paypal.me/ryanmasonjar)
21 | ## Features
22 | - Current BG
23 | - Insulin on Board (IOB)
24 | - Carbs on Board (COB)
25 | - Trend direction
26 | - Delta
27 | - Time since last pull
28 | - Graph of BG's over time
29 | - Error reporting
30 | - Temperature
31 | - Step count
32 | - Heart rate
33 | - Time
34 | - Date
35 | - Battery levels
36 | - Vibration Alerts
37 | - Changing background color
38 |
39 | ## [User Agreement](https://github.com/Rytiggy/Glance/wiki/User-Agreement)
40 | Glance must not be used to make medical decisions, by using glance you agree to the [user agreement](https://github.com/Rytiggy/Glance/wiki/User-Agreement).
41 |
42 |
--------------------------------------------------------------------------------
/website/src/components/GlanceFooter.vue:
--------------------------------------------------------------------------------
1 |
2 |
40 |
41 |
42 |
45 |
46 |
56 |
--------------------------------------------------------------------------------
/modules/app/transfer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | // Import the messaging module
17 | import * as messaging from "messaging";
18 |
19 | export default class transfer {
20 | // Send data
21 | send(data) {
22 | console.log('app - transfer - send')
23 | if (messaging.peerSocket.readyState === messaging.peerSocket.OPEN) {
24 | // Send a command to the companion
25 | messaging.peerSocket.send({
26 | command: 'forceCompanionTransfer',
27 | data: data,
28 | });
29 | }
30 | }
31 | };
32 |
33 |
34 | // Events
35 |
36 | // // Listen for the onopen event
37 | // messaging.peerSocket.onopen = function() {
38 | // // Fetch weather when the connection opens
39 | // fetchWeather();
40 | // }
41 |
42 | // Listen for messages from the companion
43 | // messaging.peerSocket.onmessage = function(evt) {
44 | // if (evt.data) {
45 | // console.log("The temperature is: " + evt.data.temperature);
46 | // }
47 | // }
48 |
49 | // // Listen for the onerror event
50 | // messaging.peerSocket.onerror = function(err) {
51 | // // Handle any errors
52 | // console.log("Connection error: " + err.code + " - " + err.message);
53 | // }
54 |
55 |
--------------------------------------------------------------------------------
/website/src/components/Gallery.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Gallery
5 |
6 |
7 | Glance is highly customizable allowing for you to change almost any
8 | aspect of the watch.
9 |
10 |
11 |
26 |
27 |
28 |
29 |
30 |
59 |
60 |
74 |
--------------------------------------------------------------------------------
/modules/companion/sizeof.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | sizeof.js
4 |
5 | A function to calculate the approximate memory usage of objects
6 |
7 | Created by Kate Morley - http://code.iamkate.com/ - and released under the terms
8 | of the CC0 1.0 Universal legal code:
9 |
10 | http://creativecommons.org/publicdomain/zero/1.0/legalcode
11 |
12 | */
13 |
14 |
15 |
16 | export default class sizeof {
17 | size(object){
18 |
19 | // initialise the list of objects and size
20 | var objects = [object];
21 | var size = 0;
22 |
23 | // loop over the objects
24 | for (var index = 0; index < objects.length; index ++){
25 |
26 | // determine the type of the object
27 | switch (typeof objects[index]){
28 |
29 | // the object is a boolean
30 | case 'boolean': size += 4; break;
31 |
32 | // the object is a number
33 | case 'number': size += 8; break;
34 |
35 | // the object is a string
36 | case 'string': size += 2 * objects[index].length; break;
37 |
38 | // the object is a generic object
39 | case 'object':
40 |
41 | // if the object is not an array, add the sizes of the keys
42 | if (Object.prototype.toString.call(objects[index]) != '[object Array]'){
43 | for (var key in objects[index]) size += 2 * key.length;
44 | }
45 |
46 | // loop over the keys
47 | for (var key in objects[index]){
48 |
49 | // determine whether the value has already been processed
50 | var processed = false;
51 | for (var search = 0; search < objects.length; search ++){
52 | if (objects[search] === objects[index][key]){
53 | processed = true;
54 | break;
55 | }
56 | }
57 |
58 | // queue the value to be processed if appropriate
59 | if (!processed) objects.push(objects[index][key]);
60 |
61 | }
62 |
63 | }
64 |
65 | }
66 |
67 | // return the calculated size
68 | return size;
69 | }
70 | };
71 |
--------------------------------------------------------------------------------
/website/src/components/GlanceHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
59 |
60 |
61 |
62 |
65 |
66 |
80 |
--------------------------------------------------------------------------------
/website/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
3 | // This option interrupts the configuration hierarchy at this file
4 | // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
5 | root: true,
6 |
7 | parserOptions: {
8 | parser: '@babel/eslint-parser',
9 | ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features
10 | sourceType: 'module' // Allows for the use of imports
11 | },
12 |
13 | env: {
14 | browser: true
15 | },
16 |
17 | // Rules order is important, please avoid shuffling them
18 | extends: [
19 | // Base ESLint recommended rules
20 | // 'eslint:recommended',
21 |
22 | // Uncomment any of the lines below to choose desired strictness,
23 | // but leave only one uncommented!
24 | // See https://eslint.vuejs.org/rules/#available-rules
25 | 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
26 | // 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
27 | // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
28 |
29 | // https://github.com/prettier/eslint-config-prettier#installation
30 | // usage with Prettier, provided by 'eslint-config-prettier'.
31 | 'prettier'
32 | ],
33 |
34 | plugins: [
35 | // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
36 | // required to lint *.vue files
37 | 'vue',
38 |
39 | // https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674
40 | // Prettier has not been included as plugin to avoid performance impact
41 | // add it as an extension for your IDE
42 |
43 | ],
44 |
45 | globals: {
46 | ga: 'readonly', // Google Analytics
47 | cordova: 'readonly',
48 | __statics: 'readonly',
49 | __QUASAR_SSR__: 'readonly',
50 | __QUASAR_SSR_SERVER__: 'readonly',
51 | __QUASAR_SSR_CLIENT__: 'readonly',
52 | __QUASAR_SSR_PWA__: 'readonly',
53 | process: 'readonly',
54 | Capacitor: 'readonly',
55 | chrome: 'readonly'
56 | },
57 |
58 | // add your custom rules here
59 | rules: {
60 |
61 | 'prefer-promise-reject-errors': 'off',
62 |
63 | // allow console.log during development only
64 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
65 |
66 | // allow debugger during development only
67 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
68 |
69 | // Most of the components use only single word names
70 | 'vue/multi-word-component-names': 'off'
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/modules/app/dateTime.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 | export default class dateTime {
15 | getDate(dateFormat, enableDOW) {
16 | console.log("app - dateTime - getDate()");
17 | let dateObj = new Date();
18 | let month = ("0" + (dateObj.getMonth() + 1)).slice(-2);
19 | let date = ("0" + dateObj.getDate()).slice(-2);
20 | let year = dateObj.getFullYear();
21 | let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
22 |
23 | if (enableDOW) {
24 | year = year.toString().substr(-2);
25 | }
26 |
27 | let shortDate = month + "/" + date + "/" + year;
28 |
29 | if (dateFormat) {
30 | if (dateFormat == "DD/MM/YYYY") {
31 | shortDate = date + "/" + month + "/" + year;
32 | } else if (dateFormat == "YYYY/MM/DD") {
33 | shortDate = year + "/" + month + "/" + date;
34 | } else if (dateFormat == "DD.MM.YYYY") {
35 | shortDate = date + "." + month + "." + year;
36 | }
37 | }
38 |
39 | if (enableDOW) {
40 | shortDate += " " + days[dateObj.getDay()];
41 | }
42 | return shortDate;
43 | }
44 |
45 | getTime(timeFormat) {
46 | console.log("app - dateTime - getTime()");
47 | let timeNow = new Date();
48 | let hh = timeNow.getHours();
49 | let mm = timeNow.getMinutes();
50 | let ss = timeNow.getSeconds();
51 | if (!timeFormat) {
52 | let formatAMPM = hh >= 12 ? "PM" : "AM";
53 | hh = hh % 12 || 12;
54 |
55 | // if(hh < 10) {
56 | // hh = '0' + hh;
57 | // }
58 | }
59 | if (mm < 10) {
60 | mm = "0" + mm;
61 | }
62 | return hh + ":" + mm;
63 | }
64 |
65 | getTimeSenseLastSGV(sgvDateTime) {
66 | console.log("app - dateTime - getTimeSenseLastSGV()");
67 | let currentTime = new Date();
68 | let lastSGVTime = new Date(sgvDateTime);
69 | let secondsDiff = (currentTime.getTime() - lastSGVTime.getTime()) / 1000;
70 | let timeSense = "";
71 | let timeSenseNumber = "";
72 | if (secondsDiff > 86400) {
73 | timeSense = "~" + Math.floor(secondsDiff / 86400) + "D";
74 | timeSenseNumber = Math.floor(secondsDiff / 60);
75 | } else if (secondsDiff > 3600) {
76 | timeSense = "~" + Math.floor(secondsDiff / 3600) + "h";
77 | timeSenseNumber = Math.floor(secondsDiff / 60);
78 | } else if (secondsDiff > 0) {
79 | timeSense = Math.floor(secondsDiff / 60) + " min";
80 | timeSenseNumber = Math.floor(secondsDiff / 60);
81 | }
82 | return [timeSense, timeSenseNumber];
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/modules/companion/fetch.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | import Logs from "./logs.js";
17 | const logs = new Logs();
18 |
19 |
20 | export default class messaging {
21 | //Fetch data from an API endpoint and return a promise
22 | async get(url) {
23 | const trimmedURL = url.replace(/ /g,"");
24 | logs.add('Line 16: companion - fetch - get() ' + trimmedURL)
25 | return await fetch(trimmedURL)
26 | .then(handleResponse)
27 | .then((data) => {
28 | logs.add(`Line 28: companion - fetch - get() Data Okay return`)
29 | return data;
30 | }).catch((error) => {
31 | // not found
32 | if(!error.status) {
33 | error.status = '404'
34 | }
35 | logs.add(`Line 35 ERROR companion - fetch - get() ${JSON.stringify(error)}`)
36 | let errorMsg = {
37 | text: 'Line 38: Error with companion - fetch - get()',
38 | error: error,
39 | url: trimmedURL,
40 | }
41 | return errorMsg;
42 | });
43 | };
44 | };
45 |
46 | function handleResponse (response) {
47 | let contentType = response.headers.get('content-type')
48 | if (contentType.includes('application/json')) {
49 | return handleJSONResponse(response)
50 | } else if (contentType.includes('text/html')) {
51 | return handleTextResponse(response)
52 | } else {
53 | // Other response types as necessary. I haven't found a need for them yet though.
54 | throw new Error(`Sorry, content-type ${contentType} not supported`)
55 | }
56 | }
57 |
58 | function handleJSONResponse (response) {
59 | return response.json()
60 | .then(json => {
61 | // console.error(JSON.stringify(json))
62 |
63 | if (response.ok) {
64 | logs.add(`Line 83 companion - fetch - handleJSONResponse() response.ok`)
65 | return json
66 | } else {
67 | return Promise.reject(Object.assign({}, json, {
68 | status: response.status,
69 | statusText: response.statusText
70 | }))
71 | }
72 | })
73 | }
74 | // This doesnt work
75 | function handleTextResponse (response) {
76 | return response.text()
77 | .then(text => {
78 | if (response.ok) {
79 | logs.add(`Line 98 companion - fetch - handleTextResponse() response.ok`)
80 | return JSON.parse(text)
81 | } else {
82 | return Promise.reject({
83 | status: response.status,
84 | statusText: response.statusText,
85 | err: text
86 | })
87 | }
88 | })
89 | }
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: ["main"]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: ["main"]
20 | schedule:
21 | - cron: "0 0 * * 1"
22 |
23 | permissions:
24 | contents: read
25 |
26 | jobs:
27 | analyze:
28 | name: Analyze
29 | runs-on: ubuntu-latest
30 | permissions:
31 | actions: read
32 | contents: read
33 | security-events: write
34 |
35 | strategy:
36 | fail-fast: false
37 | matrix:
38 | language: ["javascript"]
39 | # CodeQL supports [ $supported-codeql-languages ]
40 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
41 |
42 | steps:
43 | - name: Harden Runner
44 | uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
45 | with:
46 | egress-policy: audit
47 |
48 | - name: Checkout repository
49 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
50 |
51 | # Initializes the CodeQL tools for scanning.
52 | - name: Initialize CodeQL
53 | uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
54 | with:
55 | languages: ${{ matrix.language }}
56 | # If you wish to specify custom queries, you can do so here or in a config file.
57 | # By default, queries listed here will override any specified in a config file.
58 | # Prefix the list here with "+" to use these queries and those in the config file.
59 |
60 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
61 | # If this step fails, then you should remove it and run the build manually (see below)
62 | - name: Autobuild
63 | uses: github/codeql-action/autobuild@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
64 |
65 | # ℹ️ Command-line programs to run using the OS shell.
66 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
67 |
68 | # If the Autobuild fails above, remove it and uncomment the following three lines.
69 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
70 |
71 | # - run: |
72 | # echo "Run, Build Application using script"
73 | # ./location_of_script_within_repo/buildscript.sh
74 |
75 | - name: Perform CodeQL Analysis
76 | uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
77 | with:
78 | category: "/language:${{matrix.language}}"
79 |
--------------------------------------------------------------------------------
/.github/workflows/scorecards.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub. They are provided
2 | # by a third-party and are governed by separate terms of service, privacy
3 | # policy, and support documentation.
4 |
5 | name: Scorecard supply-chain security
6 | on:
7 | # For Branch-Protection check. Only the default branch is supported. See
8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
9 | branch_protection_rule:
10 | # To guarantee Maintained check is occasionally updated. See
11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
12 | schedule:
13 | - cron: '20 7 * * 2'
14 | push:
15 | branches: ["main"]
16 |
17 | # Declare default permissions as read only.
18 | permissions: read-all
19 |
20 | jobs:
21 | analysis:
22 | name: Scorecard analysis
23 | runs-on: ubuntu-latest
24 | permissions:
25 | # Needed to upload the results to code-scanning dashboard.
26 | security-events: write
27 | # Needed to publish results and get a badge (see publish_results below).
28 | id-token: write
29 | contents: read
30 | actions: read
31 |
32 | steps:
33 | - name: Harden Runner
34 | uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
35 | with:
36 | egress-policy: audit
37 |
38 | - name: "Checkout code"
39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
40 | with:
41 | persist-credentials: false
42 |
43 | - name: "Run analysis"
44 | uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
45 | with:
46 | results_file: results.sarif
47 | results_format: sarif
48 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
49 | # - you want to enable the Branch-Protection check on a *public* repository, or
50 | # - you are installing Scorecards on a *private* repository
51 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
52 | # repo_token: ${{ secrets.SCORECARD_TOKEN }}
53 |
54 | # Public repositories:
55 | # - Publish results to OpenSSF REST API for easy access by consumers
56 | # - Allows the repository to include the Scorecard badge.
57 | # - See https://github.com/ossf/scorecard-action#publishing-results.
58 | # For private repositories:
59 | # - `publish_results` will always be set to `false`, regardless
60 | # of the value entered here.
61 | publish_results: true
62 |
63 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
64 | # format to the repository Actions tab.
65 | - name: "Upload artifact"
66 | uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
67 | with:
68 | name: SARIF file
69 | path: results.sarif
70 | retention-days: 5
71 |
72 | # Upload the results to GitHub's code scanning dashboard.
73 | - name: "Upload to code-scanning"
74 | uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
75 | with:
76 | sarif_file: results.sarif
77 |
--------------------------------------------------------------------------------
/website/src/layouts/MyLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/website/src/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Glance
8 |
9 |
10 |
19 |
20 |
21 |
22 |
31 |
32 |
33 |
34 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
58 |
59 |
60 |
69 |
70 |
71 |
72 |
73 |
82 |
83 |
84 |
85 |
86 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
111 |
112 |
121 |
122 |
123 |
124 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/website/src/pages/Troubleshooting.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Troubleshooting
5 |
Sync Issue/Not connecting:
6 |
12 |
17 |
18 |
19 |
20 |
31 |
32 |
33 |
34 |
Common Error Codes:
35 |
36 | DSE: Data Source error - generic error with
37 |
your data source
42 |
43 | try opening Glance's settings
44 |
45 |
46 | E500: A very general HTTP status code that means something has gone
47 | wrong on your site's server but the server could not be more specific on
48 | what the exact problem is.
49 |
50 |
51 | E503: A HTTP status code that means the server is simply not available
52 | right now. Or you haven't configured your data source right.
53 |
54 |
E504: Gateway Timeout
55 |
E404: Could not locate your data source
56 |
Comunity Support:
57 |
66 |
75 |
85 |
86 |
87 |
88 |
89 |
99 |
100 |
106 |
--------------------------------------------------------------------------------
/modules/companion/dexcom.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 | import Logs from "./logs.js";
14 | const logs = new Logs();
15 |
16 | const applicationId = "d8665ade-9673-4e27-9ff6-92db4ce13d13";
17 | export default class dexcom {
18 | async login(dexcomUsername, dexcomPassword, subDomain) {
19 | // let body = {
20 | // accountName: dexcomUsername,
21 | // applicationId: "d8665ade-9673-4e27-9ff6-92db4ce13d13",
22 | // password: dexcomPassword,
23 | // };
24 |
25 | let body = {
26 | accountName: dexcomUsername,
27 | applicationId: applicationId,
28 | password: dexcomPassword,
29 | };
30 | let accountId = await fetch(
31 | `https://${subDomain}.dexcom.com/ShareWebServices/Services/General/AuthenticatePublisherAccount`,
32 | {
33 | body: JSON.stringify(body),
34 | json: true,
35 | headers: {
36 | Accept: "application/json",
37 | "Content-Type": "application/json",
38 | },
39 | method: "post",
40 | rejectUnauthorized: false,
41 | }
42 | )
43 | .then(function (response) {
44 | return response.text();
45 | })
46 | .then(function (data) {
47 | return data;
48 | })
49 | .catch((e) => console.error(e));
50 | return accountId.replace(/['"]+/g, '')
51 | }
52 |
53 | async getSessionId(dexcomUsername, dexcomPassword, subDomain) {
54 | let accountId = await this.login(dexcomUsername, dexcomPassword, subDomain);
55 | console.log(accountId);
56 |
57 | let body = {
58 | password: dexcomPassword,
59 | applicationId: applicationId,
60 | accountId: accountId,
61 | };
62 | console.log(applicationId, dexcomPassword, accountId.toString())
63 | let url = `https://${subDomain}.dexcom.com/ShareWebServices/Services/General/LoginPublisherAccountById`;
64 | let sessionId = await fetch(url, {
65 | body: JSON.stringify(body),
66 | json: true,
67 | headers: {
68 | Accept: "application/json",
69 | "Content-Type": "application/json",
70 | },
71 | method: "post",
72 | rejectUnauthorized: false,
73 | })
74 | .then(function (response) {
75 | return response.text();
76 | })
77 | .then(function (data) {
78 | return data;
79 | })
80 | .catch((e) => console.error("here", e));
81 |
82 | console.log(sessionId);
83 | return sessionId;
84 | }
85 |
86 | async getData(sessionId, subDomain) {
87 | let url =
88 | `https://${subDomain}.dexcom.com/ShareWebServices/Services/Publisher/ReadPublisherLatestGlucoseValues?sessionId=${sessionId}&minutes=1440&maxCount=47`.replace(
89 | /"/g,
90 | ""
91 | );
92 | return await fetch(url, {
93 | headers: {
94 | Accept: "application/json",
95 | "Content-Type": "application/json",
96 | },
97 | method: "post",
98 | })
99 | .then(function (response) {
100 | return response.json();
101 | })
102 | .then(function (data) {
103 | return data;
104 | })
105 | .catch((e) => console.error(e));
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/website/public/statics/icons/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/website/src/components/Donations.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Donations
5 |
6 |
7 | I developed Glance to help people with diabetes! 50% of all donations
8 | will go directly to the
9 |
Faustman lab . The remaining 50% will be dedicated to future research and
15 | development of Glance.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Donations can be made through PayPal
26 |
27 |
60 |
61 |
62 |
69 | $1
70 |
71 |
78 | $5
79 |
80 |
87 | $10
88 |
89 |
96 | Other
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
115 |
116 |
130 |
--------------------------------------------------------------------------------
/companion/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 | import { settingsStorage } from "settings";
15 |
16 | import Settings from "../modules/companion/settings.js";
17 | import Transfer from "../modules/companion/transfer.js";
18 | import Fetch from "../modules/companion/fetch.js";
19 | import Standardize from "../modules/companion/standardize.js";
20 | // import Weather from "../modules/companion/weather.js";
21 | import Logs from "../modules/companion/logs.js";
22 | import Sizeof from "../modules/companion/sizeof.js";
23 | import Dexcom from "../modules/companion/dexcom.js";
24 |
25 | import * as messaging from "messaging";
26 | import { me } from "companion";
27 |
28 | const settings = new Settings();
29 | const transfer = new Transfer();
30 | const fetch = new Fetch();
31 | const standardize = new Standardize();
32 | const dexcom = new Dexcom();
33 |
34 | // const weatherURL = new Weather();
35 | const logs = new Logs();
36 | const sizeof = new Sizeof();
37 | let dataReceivedFromWatch = null;
38 |
39 | async function sendData() {
40 | // Get settings
41 | const store = await settings.get(dataReceivedFromWatch);
42 |
43 | // Get SGV data
44 | let bloodsugars = null;
45 | let extraData = null;
46 | if (store.url === "dexcom") {
47 | let USAVSInternational = store.USAVSInternational;
48 | let subDomain = "share2";
49 | if (USAVSInternational) {
50 | subDomain = "shareous1";
51 | }
52 |
53 | let dexcomUsername = store.dexcomUsername
54 | ? store.dexcomUsername.replace(/\s+/g, "")
55 | : "";
56 | let dexcomPassword = store.dexcomPassword
57 | ? store.dexcomPassword.replace(/\s+/g, "")
58 | : "";
59 | let sessionId = await dexcom.getSessionId(
60 | dexcomUsername,
61 | dexcomPassword,
62 | subDomain
63 | );
64 | if (store.dexcomUsername && store.dexcomPassword) {
65 | bloodsugars = await dexcom.getData(sessionId, subDomain);
66 | } else {
67 | bloodsugars = {
68 | error: {
69 | status: "500",
70 | },
71 | };
72 | }
73 | } else {
74 | bloodsugars = await fetch.get(store.url);
75 | if (store.extraDataUrl) {
76 | extraData = await fetch.get(store.extraDataUrl);
77 | }
78 | }
79 |
80 | // Get weather data
81 | // let weather = await fetch.get(await weatherURL.get(store.tempType));
82 | Promise.all([bloodsugars, extraData]).then(function (values) {
83 | let dataToSend = {
84 | bloodSugars: standardize.bloodsugars(values[0], values[1], store),
85 | settings: standardize.settings(store),
86 | // weather: values[2].query.results.channel.item.condition,
87 | };
88 | logs.add(
89 | "Line 59: companion - sendData - DataToSend size: " +
90 | sizeof.size(dataToSend) +
91 | " bytes"
92 | );
93 | logs.add(
94 | "Line 60: companion - sendData - DataToSend: " +
95 | JSON.stringify(dataToSend)
96 | );
97 | transfer.send(dataToSend);
98 | });
99 | }
100 |
101 | // Listen for messages from the device
102 | messaging.peerSocket.onmessage = function (evt) {
103 | if (evt.data.command === "forceCompanionTransfer") {
104 | logs.add("Line 58: companion - Watch to Companion Transfer request");
105 | // pass in data that was recieved from the watch
106 | console.log(JSON.stringify(evt.data.data));
107 | dataReceivedFromWatch = evt.data.data;
108 | sendData();
109 | }
110 | };
111 |
112 | // Listen for the onerror event
113 | messaging.peerSocket.onerror = function (err) {
114 | // Handle any errors
115 | console.log("Connection error: " + err.code + " - " + err.message);
116 | };
117 |
118 | settingsStorage.onchange = function (evt) {
119 | logs.add("Line 70: companion - Settings changed send to watch");
120 | sendData();
121 | if (evt.key === "authorizationCode") {
122 | // Settings page sent us an oAuth token
123 | let data = JSON.parse(evt.newValue);
124 | dexcom.getAccessToken(data.name);
125 | }
126 | };
127 |
128 | const MINUTE = 1000 * 60;
129 | me.wakeInterval = 5 * MINUTE;
130 |
131 | if (me.launchReasons.wokenUp) {
132 | // The companion started due to a periodic timer
133 | console.error("Started due to wake interval!");
134 | sendData();
135 | } else {
136 | // Close the companion and wait to be awoken
137 | me.yield();
138 | }
139 | // wait 1 seconds before getting things started
140 | setTimeout(sendData, 1000);
141 |
--------------------------------------------------------------------------------
/website/src/components/Features.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Features
5 |
6 |
7 | Glance is a watchface for use with Fitbit devices to view your blood
8 | glucose levels along with a variety of other health stats on the watch
9 | face. You can see your stats at a glance!
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Current BG
20 |
21 | Receive blood glucose readings every 5 minutes.
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Vibration Alerts
34 |
35 |
36 | Vibrates to alert you when your blood glucose falls below or
37 | rises above your high or low thresholds.
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | Trend Direction
49 |
50 | See a graph of trend data for the last 4 hours to better predict
51 | where your blood glucose is heading.
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Customizations
63 |
64 | Customize Glance how you like it, change the background color,
65 | unit type (mgdl or mmol), various alerts you want to recieve,
66 | and so much more!
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | Data Source Options
78 |
79 | Select from a variety of data sources, including Dexcom ,
80 | Nightscout , Spike , xDrip and Tomato
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | Compatibility
92 |
93 | Glance is compatible with Fitbit smartwatchs that includes the
94 | Fitbit Versa , Versa lite , and Ionic .
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
108 |
122 |
--------------------------------------------------------------------------------
/resources/styles.css:
--------------------------------------------------------------------------------
1 | .text {
2 | font-size: 27;
3 | font-family: SevilleSharp-Bold;
4 | font-weight: regular;
5 | width: 100%;
6 | height: 100%;
7 | fill: white;
8 | text-length: 64;
9 | }
10 |
11 | .xs-text {
12 | font-size: 20;
13 | font-family: Seville-Condensed;
14 | font-weight: regular;
15 | width: 100%;
16 | height: 100%;
17 | fill: white;
18 | text-length: 64;
19 | }
20 |
21 | .h2 {
22 | font-size: 55;
23 | font-family: SevilleSharp-Regular;
24 | width: 100%;
25 | height: 100%;
26 | fill: white;
27 | text-length: 64;
28 | }
29 | .h1 {
30 | font-size: 95;
31 | font-family: Tungsten-Medium;
32 | width: 100%;
33 | height: 100%;
34 | fill: white;
35 | text-length: 64;
36 | }
37 |
38 | .text-gray{
39 | fill: #B8C3C4;
40 | }
41 |
42 | #battery-level {
43 | width: 30;
44 | height: 17;
45 | fill: white;
46 | x: 3%;
47 | y: 4%;
48 | }
49 |
50 | #batteryPercent {
51 | x: 13%;
52 | y: 10%+3;
53 | }
54 |
55 | .battery-image {
56 | width: 32;
57 | height: 33;
58 | x: 3%;
59 | y: 1%;
60 | }
61 |
62 | #date {
63 | x: 2%;
64 | y: 21%;
65 | }
66 |
67 |
68 | #error {
69 | x: 95%+3;
70 | y: 59%+2;
71 | }
72 |
73 | #delta {
74 | x: 98%;
75 | y: 10%;
76 | }
77 |
78 | #sgv {
79 | x: 85%;
80 | y: 28%;
81 | }
82 |
83 | #timeOfLastSgv {
84 | x: 98%;
85 | y: 40%;
86 | }
87 |
88 | #time {
89 | x: 3%;
90 | y: 50%;
91 | }
92 |
93 | #arrows {
94 | x: 87%;
95 | y: 11%;
96 | width: 45;
97 | height: 45;
98 | fill: white;
99 | }
100 |
101 | #largeGraphArrows {
102 | x: 84%;
103 | y: 11%;
104 | width: 45;
105 | height: 45;
106 | fill: white;
107 | }
108 |
109 | #weather {
110 | x: 36%;
111 | y: 10%+3;
112 | }
113 |
114 | .stat-image{
115 | width: 16;
116 | height: 16;
117 | }
118 |
119 | #graph {
120 | x: 57%-25;
121 | y: 55%;
122 | width: 175;
123 | height: 100;
124 | }
125 | .graph-point {
126 | fill:#b0b1b2;
127 | }
128 | .gval {
129 | fill:#b0b1b2;
130 | }
131 | #dismiss {
132 | y: 57%;
133 | x: 2%;
134 | width: 146;
135 | height: 100;
136 | }
137 |
138 | #goToTreatment {
139 | y: 61%;
140 | x: 2%;
141 | width: 170;
142 | height: 100;
143 | fill:black;
144 | }
145 | #goToLargeGraph {
146 | y: 23%;
147 | x: 5%;
148 | width: 170;
149 | height: 90;
150 | fill: black;
151 | }
152 |
153 | #errorLine {
154 | x1: 62%;
155 | y1: 19%+2;
156 | x2: 92%;
157 | y2: 19%+2;
158 | fill: white;
159 | }
160 |
161 | #high {
162 | x: 97%;
163 | y: 30%;
164 | width:40;
165 | }
166 | #largeGraphHigh {
167 | x: 97%;
168 | y: 30%;
169 | width:40;
170 | }
171 |
172 | #mean {
173 | x: 100%+9;
174 | y: 65;
175 | }
176 |
177 | #low {
178 | x: 97%;
179 | y: 88%;
180 | width:40;
181 | }
182 | #largeGraphLow {
183 | x: 97%;
184 | y: 88%;
185 | width:40;
186 | }
187 |
188 | #degreeIcon {
189 | width: 8;
190 | height: 8;
191 | fill: #B8C3C4;
192 | x: 44%;
193 | y: 2%;
194 | }
195 |
196 | #syringe {
197 | x: 2%;
198 | y: 53%+2;
199 | }
200 | #hamburger {
201 | x: 2%;
202 | y: 64%+2;
203 | }
204 |
205 | #steps {
206 | x: 9%+3;
207 | y: 82%+2;
208 | }
209 | #heart {
210 | x: 9%+3;
211 | y: 94%+2;
212 | }
213 |
214 | #stepIcon {
215 | x: 2%;
216 | y: 76%;
217 | }
218 |
219 | #heartIcon {
220 | x: 2%;
221 | y: 88%;
222 | }
223 |
224 | #iob {
225 | x: 9%+3;
226 | y: 60%+2;
227 | fill: white;
228 | }
229 |
230 | #cob {
231 | x: 9%+3;
232 | y: 71%+2;
233 | }
234 |
235 | #popup-title {
236 | x: 50%;
237 | y: 30%;
238 | }
239 |
240 | #carbsTreatment {
241 | x: 35%;
242 | y: 10%;
243 | width: 48%;
244 | }
245 |
246 | #insulinTreatment {
247 | x: 35%;
248 | width: 48%;
249 | }
250 |
251 | .item { height: 90; width:50; }
252 | .item text { font-size: 80; fill: white; x: 5; }
253 |
254 | .itemTenth { height: 90; width:30; }
255 | .itemTenth text { font-size: 80; fill: white; x: 0; }
256 |
257 | .treatment-image {
258 | width: 50;
259 | height: 50;
260 | }
261 |
262 | #largeGraphsSgv {
263 | text-anchor:"end";
264 | x: 85%;
265 | y: 28%;
266 | }
267 |
268 | #largeGraphArrows {
269 | x: 87%;
270 | y: 11%;
271 | width: 45;
272 | height: 45;
273 | }
274 |
275 | #largeGraphDelta {
276 | x: 98%;
277 | y: 10%
278 | }
279 | #largeGraphTimeOfLastSgv {
280 | x: 98%;
281 | y: 40%;
282 | }
283 |
284 | #rawbg {
285 | x: 75%;
286 | y: 40%;
287 | }
288 |
289 | #tempBasal {
290 | x: 3%;
291 | y: 30%;
292 | /* y: 8%+3; */
293 | }
294 |
295 | #largeGraph {
296 | y: 55%;
297 | x: 7;
298 | }
299 |
300 | #exitLargeGraph {
301 | y: 70%;
302 | x: 50%;
303 | width: 170;
304 | height: 100;
305 | fill: black;
306 | }
307 | #largeGraphSyringe {
308 | x: 3%;
309 | y: 3%;
310 | }
311 | #largeGraphHamburger {
312 | x: 3%;
313 | y: 13%
314 | }
315 | #largeGraphIob {
316 | x: 9%;
317 | y: 8%+3;
318 | /* y: 19%; */
319 | }
320 |
321 | #largeGraphCob {
322 | x: 9%;
323 | y: 19%;
324 | /* y: 29%; */
325 | }
326 |
327 | #predictedBg {
328 | x: 3%;
329 | y: 40%;
330 | }
331 |
332 | #largeGraphTime {
333 | x: 50%;
334 | y: 8%+4;
335 | }
336 |
337 | #largeGraphLoopStatus {
338 | x: 3%;
339 | y: 50%;
340 | }
341 |
342 | #largeGraphErrorLine {
343 | x1: 62%;
344 | y1: 19%+2;
345 | x2: 92%;
346 | y2: 19%+2;
347 | fill: white;
348 | }
349 |
350 | #alertArrows {
351 | x: 65%;
352 | y: 14%;
353 | width: 45;
354 | height: 45;
355 | fill: white;
356 | }
357 |
--------------------------------------------------------------------------------
/resources/styles~300x300.css:
--------------------------------------------------------------------------------
1 | .text {
2 | font-size: 23;
3 | font-family: Colfax-Medium;
4 | font-weight: regular;
5 | width: 100%;
6 | height: 100%;
7 | fill: white;
8 | text-length: 64;
9 | }
10 |
11 | .xs-text {
12 | font-size: 15;
13 | font-family: Seville-Condensed;
14 | font-weight: regular;
15 | width: 100%;
16 | height: 100%;
17 | fill: white;
18 | text-length: 64;
19 | }
20 |
21 | .h2 {
22 | font-size: 60;
23 | font-family: SevilleSharp-Regular;
24 | width: 100%;
25 | height: 100%;
26 | fill: white;
27 | text-length: 64;
28 | }
29 | .h1 {
30 | font-size: 110;
31 | font-family: Tungsten-Medium;
32 | width: 100%;
33 | height: 100%;
34 | fill: white;
35 | text-length: 64;
36 | }
37 |
38 | .text-gray{
39 | fill: #B8C3C4;
40 | }
41 |
42 | #battery-level {
43 | width: 30;
44 | height: 15;
45 | fill: white;
46 | x: 3%;
47 | y: 4%;
48 | }
49 |
50 | .battery-image {
51 | width: 32;
52 | height: 33;
53 | x: 3%;
54 | y: 1%;
55 | }
56 | #batteryPercent {
57 | /* x: 13%-2;
58 | y: 7%+2; */
59 | x: 15%;
60 | y: 8%+3;
61 | /* font-size: 11;
62 | fill: #7f7f7f;
63 | font-family: Colfax-Medium; */
64 | }
65 |
66 | #date {
67 | x: 3%;
68 | y: 18%;
69 | }
70 |
71 | #error {
72 | x: 95%+3;
73 | y: 59%+2;
74 | }
75 |
76 |
77 | #iob {
78 | x: 11%+3;
79 | y: 64%+2;
80 | }
81 |
82 | #cob {
83 | x: 11%+3;
84 | y: 75%;
85 | }
86 |
87 | #delta {
88 | x: 97%;
89 | y: 8%+3;
90 | }
91 |
92 |
93 | #sgv {
94 | text-anchor:"end";
95 | x: 97%;
96 | y: 26%;
97 | }
98 |
99 | #timeOfLastSgv{
100 | x: 97%;
101 | y: 33%;
102 | }
103 |
104 | #time {
105 | x: 3%;
106 | y: 47%;
107 | }
108 |
109 | #arrows {
110 | x: 83%;
111 | y: 32%;
112 | width: 45;
113 | height: 45;
114 | fill: white;
115 | }
116 |
117 | #weather {
118 | x: 36%;
119 | y: 8%+3;
120 | }
121 |
122 | #steps {
123 | x: 10%+3;
124 | y: 85%+2;
125 | }
126 | #heart {
127 | x: 10%+3;
128 | y: 95%;
129 | }
130 |
131 | #stepIcon {
132 | x: 3%;
133 | y: 80%;
134 | }
135 | #heartIcon {
136 | x: 3%;
137 | y: 90%;
138 | }
139 | .stat-image{
140 | width: 16;
141 | height: 16;
142 | }
143 |
144 | #graph {
145 | x: 49%-24;
146 | y: 60%+3;
147 | width: 175;
148 | height: 100;
149 | }
150 |
151 | .graph-point {
152 | fill:#b0b1b2;
153 | }
154 |
155 | .gval {
156 | fill:#b0b1b2;
157 | }
158 |
159 | #dismiss {
160 | y: 59%;
161 | x: 5%;
162 | width: 100;
163 | height: 115;
164 | }
165 |
166 | #goToTreatment {
167 | y: 66%;
168 | x: 2%;
169 | width: 145;
170 | height: 100;
171 | fill: black;
172 | }
173 | #goToLargeGraph {
174 | y: 25%;
175 | x: 20%;
176 | width: 145;
177 | height: 90;
178 | fill: black;
179 | }
180 |
181 | #errorLine {
182 | x1: 68%;
183 | y1: 19%;
184 | x2: 96%;
185 | y2: 19%;
186 | fill: white;
187 | }
188 |
189 | #high {
190 | x: 97%;
191 | y: 30%;
192 | width: 40;
193 | }
194 | #largeGraphHigh {
195 | x: 98%;
196 | y: 30%;
197 | width: 40;
198 | }
199 |
200 | #mean {
201 | x: 100%+9;
202 | y: 65;
203 | }
204 |
205 | #low {
206 | x: 97%;
207 | y: 88%;
208 | width: 40;
209 | }
210 | #largeGraphLow {
211 | x: 97%;
212 | y: 88%;
213 | width: 40;
214 | }
215 |
216 | #degreeIcon {
217 | width: 8;
218 | height: 8;
219 | fill: #B8C3C4;
220 | x: 45%;
221 | y: 2%;
222 | }
223 |
224 | #syringe {
225 | x: 3%;
226 | y: 60%;
227 | }
228 | #hamburger {
229 | x: 3%;
230 | y: 70%;
231 | }
232 | #popup-title {
233 | x: 50%;
234 | y: 30%
235 | }
236 |
237 | #carbsTreatment {
238 | x: 50%;
239 | y: 27%;
240 | width: 50%;
241 | }
242 |
243 | #insulinTreatment {
244 | x: 50%;
245 | y: $;
246 | width: 50%;
247 | }
248 |
249 | #graphPoints {
250 | fill: red;
251 | }
252 |
253 | .item { height: 90; width:50; }
254 | .item text { font-size: 80; fill: white; x: 10; }
255 |
256 | .itemTenth { height: 90; width:30; }
257 | .itemTenth text { font-size: 80; fill: white; x: 0; }
258 |
259 | .treatment-image {
260 | width: 50;
261 | height: 50;
262 | }
263 |
264 | #largeGraphsSgv {
265 | text-anchor:"end";
266 | x: 79%;
267 | y: 25%;
268 | }
269 |
270 | #largeGraphArrows {
271 | x: 83%;
272 | y: 11%;
273 | width: 45;
274 | height: 45;
275 | fill: white;
276 | }
277 |
278 | #largeGraphDelta {
279 | x: 97%;
280 | y: 8%+3;
281 | }
282 | #largeGraphTimeOfLastSgv {
283 | x: 97%;
284 | y: 33%;
285 | }
286 |
287 | #rawbg {
288 | x: 97%;
289 | y: 40%;
290 | }
291 |
292 | #largeGraph {
293 | y: 61%;
294 | x: 3%;
295 | width:97%;
296 | }
297 |
298 | #exitLargeGraph {
299 | y: 67%;
300 | x: 43%;
301 | width: 170;
302 | height: 100;
303 | fill: black;
304 | }
305 |
306 |
307 | #largeGraphSyringe {
308 | x: 3%;
309 | y: 4%;
310 | }
311 | #largeGraphHamburger {
312 | x: 3%;
313 | y: 12%;
314 | }
315 | #largeGraphIob {
316 | x: 10%;
317 | y: 8%+4;
318 | }
319 |
320 | #largeGraphCob {
321 | x: 10%;
322 | y: 17%;
323 | }
324 |
325 | #predictedBg {
326 | x: 3%;
327 | y: 25%;
328 | }
329 |
330 | #tempBasal {
331 | x: 3%;
332 | y: 33%;
333 | }
334 |
335 | #largeGraphTime {
336 | x: 50%;
337 | y: 8%+4;
338 | }
339 |
340 | #largeGraphLoopStatus {
341 | x: 3%;
342 | y: 40%;
343 | }
344 |
345 | #largeGraphErrorLine {
346 | x1: 52%;
347 | y1: 19%;
348 | x2: 79%;
349 | y2: 19%;
350 | fill: white;
351 | }
352 |
353 | #alertArrows {
354 | x: 70%;
355 | y: 15%;
356 | width: 45;
357 | height: 45;
358 | fill: white;
359 | }
360 |
--------------------------------------------------------------------------------
/resources/styles~336x336.css:
--------------------------------------------------------------------------------
1 | .text {
2 | font-size: 23;
3 | font-family: Colfax-Medium;
4 | font-weight: regular;
5 | width: 100%;
6 | height: 100%;
7 | fill: white;
8 | text-length: 64;
9 | }
10 |
11 | .xs-text {
12 | font-size: 15;
13 | font-family: Seville-Condensed;
14 | font-weight: regular;
15 | width: 100%;
16 | height: 100%;
17 | fill: white;
18 | text-length: 64;
19 | }
20 |
21 | .h3 {
22 | font-size: 45;
23 | font-family: Colfax-Medium;
24 | font-weight: regular;
25 | width: 100%;
26 | height: 100%;
27 | fill: white;
28 | text-length: 64;
29 | }
30 | .h2 {
31 | font-size: 60;
32 | font-family: SevilleSharp-Regular;
33 | width: 100%;
34 | height: 100%;
35 | fill: white;
36 | text-length: 64;
37 | }
38 | .h1 {
39 | font-size: 120;
40 | font-family: Tungsten-Medium;
41 | width: 100%;
42 | height: 100%;
43 | fill: white;
44 | text-length: 64;
45 | }
46 |
47 | .text-gray{
48 | fill: #B8C3C4;
49 | }
50 |
51 | #battery-level {
52 | width: 30;
53 | height: 18;
54 | fill: #75bd78;
55 | x: 15%;
56 | y: 5%+3;
57 | }
58 |
59 | .battery-image {
60 | width: 32;
61 | height: 34;
62 | x: 15%;
63 | y: 3%;
64 | }
65 | #batteryPercent {
66 | x: 25%;
67 | y: 11%;
68 | }
69 |
70 | #date {
71 | x: 6%;
72 | y: 18%;
73 | }
74 |
75 | #error {
76 | x: 95%+3;
77 | y: 59%+2;
78 | }
79 |
80 |
81 | #iob {
82 | x: 9%;
83 | y: 58%;
84 | }
85 |
86 | #cob {
87 | x: 9%;
88 | y: 67%;
89 | }
90 |
91 | #delta {
92 | x: 85%;
93 | y: 11%;
94 | }
95 |
96 | #sgv {
97 | text-anchor:"end";
98 | x: 95%;
99 | y: 28%;
100 | }
101 |
102 | #timeOfLastSgv{
103 | x: 94%;
104 | y: 35%;
105 | }
106 |
107 | #time {
108 | x: 3%;
109 | y: 47%;
110 | }
111 |
112 | #arrows {
113 | x: 83%;
114 | y: 35%;
115 | width: 45;
116 | height: 45;
117 | fill: white;
118 | }
119 |
120 | #weather {
121 | x: 36%;
122 | y: 8%+3;
123 | opacity: 0;
124 | }
125 |
126 | #steps {
127 | x: 9%;
128 | y: 76%;
129 | }
130 | #heart {
131 | x: 9%;
132 | y: 85%;
133 | }
134 |
135 | #stepIcon {
136 | x: 3%;
137 | y: 71%;
138 | }
139 | #heartIcon {
140 | x: 3%;
141 | y: 80%;
142 | }
143 | .stat-image{
144 | width: 16;
145 | height: 16;
146 | }
147 |
148 | #graph {
149 | x: 49%-24;
150 | y: 60%+3;
151 | width: 175;
152 | height: 100;
153 | }
154 |
155 | .graph-point {
156 | fill:#b0b1b2;
157 | }
158 |
159 | .gval {
160 | fill:#b0b1b2;
161 | }
162 |
163 | #dismiss {
164 | y: 59%;
165 | x: 5%;
166 | width: 100;
167 | height: 115;
168 | }
169 |
170 | #dismiss #text {
171 | fill:white;
172 | }
173 |
174 | #goToTreatment {
175 | y: 66%;
176 | x: 2%;
177 | width: 145;
178 | height: 100;
179 | fill: black;
180 | }
181 | #goToLargeGraph {
182 | y: 25%;
183 | x: 20%;
184 | width: 145;
185 | height: 90;
186 | fill: black;
187 | }
188 |
189 | #errorLine {
190 | x1: 68%;
191 | y1: 19%;
192 | x2: 96%;
193 | y2: 19%;
194 | fill: white;
195 | }
196 |
197 | #high {
198 | x: 97%;
199 | y: 30%;
200 | width: 40;
201 | }
202 | #largeGraphHigh {
203 | x: 98%;
204 | y: 30%;
205 | width: 40;
206 | }
207 |
208 | #mean {
209 | x: 100%+9;
210 | y: 65;
211 | }
212 |
213 | #low {
214 | x: 97%;
215 | y: 88%;
216 | width: 40;
217 | }
218 | #largeGraphLow {
219 | x: 97%;
220 | y: 88%;
221 | width: 40;
222 | }
223 |
224 | #degreeIcon {
225 | width: 8;
226 | height: 8;
227 | fill: #B8C3C4;
228 | x: 45%;
229 | y: 2%;
230 | opacity: 0;
231 | }
232 |
233 | #syringe {
234 | x: 3%;
235 | y: 53%;
236 | }
237 | #hamburger {
238 | x: 3%;
239 | y: 62%;
240 | }
241 | #popup-title {
242 | x: 50%;
243 | y: 30%
244 | }
245 |
246 | #carbsTreatment {
247 | x: 50%;
248 | y: 27%;
249 | width: 50%;
250 | }
251 |
252 | #insulinTreatment {
253 | x: 50%;
254 | y: $;
255 | width: 50%;
256 | }
257 |
258 | #graphPoints {
259 | fill: red;
260 | }
261 |
262 | .item { height: 90; width:50; }
263 | .item text { font-size: 80; fill: white; x: 10; }
264 |
265 | .itemTenth { height: 90; width:30; }
266 | .itemTenth text { font-size: 80; fill: white; x: 0; }
267 |
268 | .treatment-image {
269 | width: 50;
270 | height: 50;
271 | }
272 |
273 | #largeGraphsSgv {
274 | text-anchor:"end";
275 | x: 95%;
276 | y: 28%;
277 | }
278 |
279 |
280 | #largeGraphArrows {
281 | x: 83%;
282 | y: 35%;
283 | width: 45;
284 | height: 45;
285 | fill: white;
286 | }
287 |
288 | #largeGraphDelta {
289 | x: 85%;
290 | y: 11%;
291 | }
292 | #largeGraphTimeOfLastSgv {
293 | x: 94%;
294 | y: 35%;
295 | }
296 |
297 | #rawbg {
298 | x: 75%;
299 | y: 35%;
300 | }
301 |
302 | #largeGraph {
303 | y: 61%;
304 | x: 3%;
305 | width:97%;
306 | }
307 |
308 | #exitLargeGraph {
309 | y: 67%;
310 | x: 43%;
311 | width: 170;
312 | height: 100;
313 | fill: black;
314 | }
315 |
316 |
317 | #largeGraphSyringe {
318 | x: 3%;
319 | y: 53%;
320 | }
321 | #largeGraphHamburger {
322 | x: 3%;
323 | y: 62%;
324 | }
325 | #largeGraphIob {
326 | x: 9%;
327 | y: 58%;
328 | }
329 |
330 | #largeGraphCob {
331 | x: 9%;
332 | y: 67%;
333 | }
334 |
335 | #predictedBg {
336 | x: 3%;
337 | y: 25%;
338 | }
339 |
340 | #tempBasal {
341 | x: 3%;
342 | y: 33%;
343 | }
344 |
345 | #largeGraphTime {
346 | x: 20%;
347 | y: 15%;
348 | }
349 |
350 | #largeGraphLoopStatus {
351 | x: 3%;
352 | y: 40%;
353 | }
354 |
355 | #largeGraphErrorLine {
356 | x1: 52%;
357 | y1: 19%;
358 | x2: 79%;
359 | y2: 19%;
360 | fill: white;
361 | }
362 |
363 | #alertArrows {
364 | x: 70%;
365 | y: 15%;
366 | width: 45;
367 | height: 45;
368 | fill: white;
369 | }
370 |
--------------------------------------------------------------------------------
/website/quasar.conf.js:
--------------------------------------------------------------------------------
1 | // Configuration for your app
2 | // https://quasar.dev/quasar-cli/quasar-conf-js
3 |
4 | const ESLintPlugin = require('eslint-webpack-plugin')
5 |
6 | module.exports = function (ctx) {
7 | return {
8 | // app boot file (/src/boot)
9 | // --> boot files are part of "main.js"
10 | boot: ["i18n", "axios"],
11 |
12 | css: ["app.styl"],
13 |
14 | extras: [
15 | // 'ionicons-v4',
16 | // 'mdi-v3',
17 | 'fontawesome-v5',
18 | // 'eva-icons',
19 | // 'themify',
20 | // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
21 |
22 | "roboto-font", // optional, you are not bound to it
23 | "material-icons" // optional, you are not bound to it
24 | ],
25 |
26 | framework: {
27 | // iconSet: 'ionicons-v4',
28 | // lang: 'de', // Quasar language
29 |
30 | // all: true, // --- includes everything; for dev only!
31 | cssAddon: true,
32 | components: [
33 | "QLayout",
34 | "QHeader",
35 | "QDrawer",
36 | "QPageContainer",
37 | "QPage",
38 | "QToolbar",
39 | "QToolbarTitle",
40 | "QBtn",
41 | "QIcon",
42 | "QList",
43 | "QItem",
44 | "QItemSection",
45 | "QItemLabel",
46 | "QCarousel",
47 | "QCarouselControl",
48 | "QCarouselSlide",
49 | "QPageSticky",
50 | "QParallax",
51 | "QAvatar",
52 | "QCard",
53 | "QCardSection",
54 | "QCardActions",
55 | "QChip",
56 | "QImg",
57 | "QSeparator",
58 | "QBtnGroup",
59 | "QStepper",
60 | "QStep",
61 | "QStepperNavigation",
62 | "QTabs",
63 | "QTab",
64 | "QRouteTab",
65 | "QTabPanel",
66 | "QTabPanels",
67 | "QImg",
68 | "QBadge",
69 | "QSpace",
70 | "QBtnDropdown"
71 | ],
72 |
73 | directives: ["Ripple"],
74 |
75 | // Quasar plugins
76 | plugins: ["Notify"]
77 | },
78 |
79 | supportIE: true,
80 |
81 | build: {
82 | scopeHoisting: true,
83 | vueRouterMode: 'history',
84 | // vueCompiler: true,
85 | // gzip: true,
86 | // analyze: true,
87 | // extractCSS: false,
88 |
89 | chainWebpack (chain) {
90 | chain.plugin('eslint-webpack-plugin')
91 | .use(ESLintPlugin, [{ extensions: [ 'js', 'vue' ] }])
92 | }
93 | },
94 |
95 | devServer: {
96 | // https: true,
97 | // port: 8080,
98 | open: true // opens browser window automatically
99 | },
100 |
101 | // animations: 'all', // --- includes all animations
102 | animations: [],
103 |
104 | ssr: {
105 | pwa: false,
106 |
107 | chainWebpackWebserver (chain) {
108 | chain.plugin('eslint-webpack-plugin')
109 | .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
110 | },
111 | },
112 |
113 | pwa: {
114 | // workboxPluginMode: 'InjectManifest',
115 | // workboxOptions: {}, // only for NON InjectManifest
116 |
117 | chainWebpackCustomSW (chain) {
118 | chain.plugin('eslint-webpack-plugin')
119 | .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
120 | },
121 |
122 | manifest: {
123 | // name: 'Glance Watchface',
124 | // short_name: 'Glance Watchface',
125 | // description: 'Glance is a application for use with Fitbit devices to view your blood glucose levels along with a variety of other health stats on the watch face. You can see your stats at a glance!',
126 | display: "standalone",
127 | orientation: "portrait",
128 | background_color: "#ffffff",
129 | theme_color: "#027be3",
130 | icons: [
131 | {
132 | src: "statics/icons/icon-128x128.png",
133 | sizes: "128x128",
134 | type: "image/png"
135 | },
136 | {
137 | src: "statics/icons/icon-192x192.png",
138 | sizes: "192x192",
139 | type: "image/png"
140 | },
141 | {
142 | src: "statics/icons/icon-256x256.png",
143 | sizes: "256x256",
144 | type: "image/png"
145 | },
146 | {
147 | src: "statics/icons/icon-384x384.png",
148 | sizes: "384x384",
149 | type: "image/png"
150 | },
151 | {
152 | src: "statics/icons/icon-512x512.png",
153 | sizes: "512x512",
154 | type: "image/png"
155 | }
156 | ]
157 | }
158 | },
159 |
160 | cordova: {
161 | // id: 'org.cordova.quasar.app',
162 | // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
163 | },
164 |
165 | electron: {
166 | // bundler: 'builder', // or 'packager'
167 |
168 | extendWebpack(cfg) {
169 | // do something with Electron main process Webpack cfg
170 | // chainWebpack also available besides this extendWebpack
171 | },
172 |
173 | packager: {
174 | // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
175 | // OS X / Mac App Store
176 | // appBundleId: '',
177 | // appCategoryType: '',
178 | // osxSign: '',
179 | // protocol: 'myapp://path',
180 | // Windows only
181 | // win32metadata: { ... }
182 | },
183 |
184 | builder: {
185 | // https://www.electron.build/configuration/configuration
186 | // appId: 'glance'
187 | }
188 | }
189 | };
190 | }
191 |
--------------------------------------------------------------------------------
/modules/app/alerts.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 | import document from "document";
15 | import { vibration } from "haptics";
16 | import Transfer from "./transfer.js";
17 |
18 | import DateTime from "./dateTime.js";
19 |
20 | const transfer = new Transfer();
21 |
22 | let sgv = document.getElementById("sgv");
23 | let largeGraphsSgv = document.getElementById("largeGraphsSgv");
24 | let errorLine = document.getElementById("errorLine");
25 | // let largeGraphErrorLine = document.getElementById("largeGraphErrorLine");
26 | let popup = document.getElementById("popup");
27 | let alertHeader = document.getElementById("alertHeader");
28 | let dismiss = popup.getElementById("dismiss");
29 | let popupTitle = document.getElementById("popup-title");
30 | let alertArrows = document.getElementById("alertArrows");
31 | let popupLeadText = popup.getElementById("popup-title");
32 |
33 | const dateTime = new DateTime();
34 |
35 | export default class alerts {
36 | check(bg, settings, DISABLE_ALERTS, timeSenseLastSGV) {
37 | let currentBG = bg.currentbg;
38 | let loopstatus = bg.loopstatus;
39 | let staleData =
40 | parseInt(timeSenseLastSGV, 10) >= settings.staleDataAlertAfter; // Boolean true if timeSenseLastSGV > 15
41 |
42 | alertArrows.href = "../resources/img/arrows/" + bg.direction + ".png";
43 | alertArrows.style.display = "inline";
44 | console.log("app - Alerts - Check()");
45 | sgv.style.fill = "#75bd78";
46 | largeGraphsSgv.style.fill = "#75bd78";
47 | errorLine.style.fill = "#75bd78";
48 | // largeGraphErrorLine.style.fill ="#75bd78";
49 | popupLeadText.text = "Check Blood Sugar!";
50 |
51 | let timeSenseLastSGV = dateTime.getTimeSenseLastSGV(bg.datetime)[1];
52 | if (bg.sgv <= parseInt(settings.lowThreshold) && !staleData) {
53 | if (!settings.disableAlert) {
54 | if (!DISABLE_ALERTS) {
55 | if (settings.lowAlerts) {
56 | if (timeSenseLastSGV <= 8) {
57 | console.log("low BG");
58 | vibration.start("ring");
59 | popup.style.display = "inline";
60 | popupTitle.style.display = "inline";
61 | popupTitle.text = currentBG;
62 | }
63 | }
64 | }
65 | }
66 | sgv.style.fill = "#de4430";
67 | largeGraphsSgv.style.fill = "#de4430";
68 |
69 | popupTitle.style.fill = "#de4430";
70 | errorLine.style.fill = "#de4430";
71 | // largeGraphErrorLine.style.fill ="#de4430";
72 | }
73 | if (bg.sgv >= parseInt(settings.highThreshold) && !staleData) {
74 | if (!settings.disableAlert) {
75 | if (!DISABLE_ALERTS) {
76 | if (settings.highAlerts) {
77 | if (timeSenseLastSGV <= 8) {
78 | console.log("high BG");
79 | vibration.start("ring");
80 | popup.style.display = "inline";
81 | popupTitle.style.display = "inline";
82 | popupTitle.text = currentBG;
83 | }
84 | }
85 | }
86 | }
87 | sgv.style.fill = "orange";
88 | largeGraphsSgv.style.fill = "orange";
89 |
90 | popupTitle.style.fill = "orange";
91 | errorLine.style.fill = "orange";
92 | // largeGraphErrorLine.style.fill ="orange";
93 | if (bg.sgv >= parseInt(settings.highThreshold) + 35) {
94 | sgv.style.fill = "#de4430";
95 | largeGraphsSgv.style.fill = "#de4430";
96 | popupTitle.style.fill = "#de4430";
97 | errorLine.style.fill = "#de4430";
98 | // largeGraphErrorLine.style.fill ="#de4430";
99 | }
100 | }
101 |
102 | /**
103 | * loopstatus
104 | */
105 | if (loopstatus === "Warning" && !staleData) {
106 | if (!settings.disableAlert) {
107 | if (!DISABLE_ALERTS) {
108 | if (settings.loopstatus) {
109 | console.log("loopstatus");
110 | alertArrows.style.display = "none";
111 | popupTitle.style.fill = "#de4430";
112 | vibration.start("ring");
113 | popup.style.display = "inline";
114 | popupTitle.style.display = "inline";
115 | popupTitle.text = loopstatus;
116 | popupLeadText.text = "Loop Status";
117 | }
118 | }
119 | }
120 | }
121 |
122 | // Check for rapid change in bg
123 | if (bg.direction === "DoubleDown" && !staleData) {
124 | if (!settings.disableAlert) {
125 | if (!DISABLE_ALERTS) {
126 | if (settings.rapidFall) {
127 | alertArrows.style.display = "none";
128 | console.log("Double Down");
129 | popupTitle.style.fill = "#de4430";
130 | vibration.start("ring");
131 | popup.style.display = "inline";
132 | popupTitle.style.display = "inline";
133 | popupTitle.text = "Rapid Fall!";
134 | }
135 | }
136 | }
137 | } else if (bg.direction === "DoubleUp" && !staleData) {
138 | if (!settings.disableAlert) {
139 | if (!DISABLE_ALERTS) {
140 | if (settings.rapidRise) {
141 | alertArrows.style.display = "none";
142 | console.log("Double Up");
143 | popupTitle.style.fill = "#de4430";
144 | vibration.start("ring");
145 | popup.style.display = "inline";
146 | popupTitle.style.display = "inline";
147 | popupTitle.text = "Rapid Rise!";
148 | }
149 | }
150 | }
151 | }
152 |
153 | // check if stale data
154 | if (staleData) {
155 | if (!settings.disableAlert) {
156 | if (!DISABLE_ALERTS) {
157 | if (settings.staleData) {
158 | alertArrows.style.display = "none";
159 | popupTitle.style.fill = "#de4430";
160 | vibration.start("ring");
161 | popup.style.display = "inline";
162 | popupTitle.style.display = "inline";
163 | popupTitle.text = "Stale data";
164 | }
165 | }
166 | }
167 | }
168 | }
169 | stop() {
170 | console.log("app - Alerts - stop()");
171 | vibration.stop();
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/modules/app/bloodline.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 |
15 |
16 | import { me as device } from "device";
17 | import document from "document";
18 |
19 | let highNumber = document.getElementById("high");
20 | let lowNumber = document.getElementById("low");
21 | let largeGraphHigh = document.getElementById("largeGraphHigh");
22 | let largeGraphLow = document.getElementById("largeGraphLow");
23 |
24 | let meanNumber = document.getElementsByClassName("mean");
25 | let highLine = document.getElementsByClassName("highLine");
26 | let meanLine = document.getElementsByClassName("meanLine");
27 | let lowLine = document.getElementsByClassName("lowLine");
28 |
29 | let graphPoints = document.getElementsByClassName("graphPoints");
30 | let largeGraphGraphPoints = document.getElementsByClassName("largeGraphGraphPoints");
31 |
32 | export default class bloodline {
33 | update(bloodsugars, high, low, settings) {
34 | let isMmol = settings.glucoseUnits === 'mmol';
35 |
36 | console.log('app - bloodline - update()')
37 | let reverseBloodsugars = bloodsugars.reverse();
38 |
39 | let predictedValues = reverseBloodsugars.filter((bg) => {
40 | if (bg.p) {
41 | return bg;
42 | }
43 | });
44 | let smallReverseBloodsugars = reverseBloodsugars.filter((bg,index) => {
45 | // bg.p loop = 18 40 ar2 = 5 28
46 | if(!settings.enableSmallGraphPrediction && !(bg.p) && index >= (reverseBloodsugars.length - (predictedValues.length + 24)) ) {
47 | return bg;
48 | } else if (index >= (reverseBloodsugars.length - 24)) {
49 | return bg;
50 | }
51 | });
52 |
53 | let ymin = low;
54 | let ymax = high;
55 | let height = 100;
56 | // map all sgv to an array then filter out LOS values
57 | let sgvArray = reverseBloodsugars.map(bg => bg.sgv).filter(bg => bg !== 'LOS');
58 | const currentHighestBg = Math.max(...sgvArray);
59 | const currentLowBg = Math.min(...sgvArray);
60 | if(currentHighestBg >= 400) {
61 | ymax = 340;
62 | }
63 | if(currentHighestBg >= 350 && currentHighestBg < 400) {
64 | ymax = 300;
65 | }
66 | if(currentHighestBg >= 300 && currentHighestBg < 350) {
67 | ymax = 270;
68 | }
69 | if(currentHighestBg >= 250 && currentHighestBg < 300) {
70 | ymax = 260;
71 | }
72 | if(currentHighestBg >= 220 && currentHighestBg < 250) {
73 | ymax = 220;
74 | if( high >= 220) {
75 | ymax = 250;
76 | }
77 | }
78 | if(currentHighestBg >= 160 && currentHighestBg < 220) {
79 | ymax = 180;
80 | if( high >= 220) {
81 | ymax = 240;
82 | }
83 | }
84 | if(currentLowBg < 60) {
85 | ymin = 60;
86 | }
87 | if(currentLowBg < 50) {
88 | ymin = 50;
89 | }
90 | if(currentLowBg < 40) {
91 | ymin = 40;
92 | }
93 |
94 | let highY = (height - (height * (Math.round(((high - ymin) / (ymax - ymin)) * 100) / 100)));
95 | let lowY = (height - (height * (Math.round(((low - ymin) / (ymax - ymin)) * 100) / 100)));
96 | highLine[0].y1 = highY;
97 | highLine[0].y2 = highY;
98 | meanLine[0].y1 = (highY + lowY)/2;
99 | meanLine[0].y2 = (highY + lowY)/2;
100 | lowLine[0].y1 = lowY;
101 | lowLine[0].y2 = lowY;
102 |
103 | highLine[1].y1 = highY;
104 | highLine[1].y2 = highY;
105 | meanLine[1].y1 = (highY + lowY)/2;
106 | meanLine[1].y2 = (highY + lowY)/2;
107 | lowLine[1].y1 = lowY;
108 | lowLine[1].y2 = lowY;
109 |
110 | highNumber.y = highY;
111 | lowNumber.y = lowY;
112 |
113 | largeGraphHigh.y = highY;
114 | largeGraphLow.y = lowY;
115 |
116 | let tempHigh = high;
117 | let tempLow = low;
118 | let tempMean = (high + low)/2;
119 | if (isMmol) {
120 | tempHigh = mmol(tempHigh);
121 | tempLow = mmol(tempLow);
122 | }
123 |
124 | highNumber.text = tempHigh;
125 | lowNumber.text = tempLow;
126 | largeGraphHigh.text = tempHigh;
127 | largeGraphLow.text = tempLow;
128 |
129 | // loop over bloodsugars and plot graph points
130 | // 22 loops
131 | graphPoints.forEach((point, index) => {
132 | try {
133 | let bg = smallReverseBloodsugars[index];
134 | if(smallReverseBloodsugars[index].sgv === 'LOS') {
135 | graphPoints[index].style.opacity = 0;
136 | } else {
137 | graphPoints[index].style.opacity = 1;
138 | let pointY = (height - (height * (Math.round(((bg.sgv - ymin) / (ymax - ymin)) * 100) / 100)));
139 | // - TODO: compare time of current sgv to time of last sgv and make sure its equal 5m if not add spacing
140 | graphPoints[index].cy = pointY;
141 | graphPoints[index].style.fill = "#708090"; // gray
142 | // - check sgv point is in range if not change color
143 | if(bg.p) {
144 | graphPoints[index].style.fill = "#f76ac5"; // pink
145 | // graphPoints[index].r = 3;
146 | } else if (parseInt(bg.sgv, 10) <= low){
147 | graphPoints[index].style.fill = "#de4430"; //red
148 | } else if ( parseInt(bg.sgv, 10) >= high) {
149 | graphPoints[index].style.fill = "orange"; // orange
150 | if ( parseInt(bg.sgv, 10) >= (parseInt(high) + 35)) {
151 | graphPoints[index].style.fill = "#de4430"; // red
152 | }
153 | } else {
154 | graphPoints[index].style.fill = "#75bd78"; // green
155 | }
156 | }
157 | } catch(e) {
158 | console.error(e)
159 | }
160 |
161 |
162 | });
163 |
164 | // 47 loops
165 | for (let index = 0; index < reverseBloodsugars.length; index++) {
166 | if(reverseBloodsugars[index].sgv === 'LOS') {
167 | largeGraphGraphPoints[index].style.opacity = 0;
168 | } else {
169 | largeGraphGraphPoints[index].style.opacity = 1;
170 | let pointY = (height - (height * (Math.round(((reverseBloodsugars[index].sgv - ymin) / (ymax - ymin)) * 100) / 100)));
171 | // - TODO: compare time of current sgv to time of last sgv and make sure its equal 5m if not add spacing
172 | largeGraphGraphPoints[index].cy = pointY;
173 | largeGraphGraphPoints[index].style.fill = "#708090"; // gray
174 | // - check sgv point is in range if not change color
175 | if(reverseBloodsugars[index].p) {
176 | largeGraphGraphPoints[index].style.fill = "#f76ac5"; // pink
177 | } else if (parseInt(reverseBloodsugars[index].sgv, 10) <= low){
178 | //- INFO: largeGraphGraphPoints has to be at the 22 index becase it is ALL the points on both graphs combined
179 | largeGraphGraphPoints[index].style.fill = "#de4430"; //red
180 | } else if ( parseInt(reverseBloodsugars[index].sgv, 10) >= high) {
181 | largeGraphGraphPoints[index].style.fill = "orange"; // orange
182 | if ( parseInt(reverseBloodsugars[index].sgv, 10) >= (parseInt(high) + 35)) {
183 | largeGraphGraphPoints[index].style.fill = "#de4430"; // red
184 | }
185 | } else {
186 | largeGraphGraphPoints[index].style.fill = "#75bd78"; // green
187 | }
188 | }
189 | }
190 | reverseBloodsugars.reverse();
191 | }
192 | };
193 |
194 | // converts a mg/dL to mmoL
195 | function mmol(bg , roundToHundredths) {
196 | let mmolBG = (Math.round((bg / 18) * 10) / 10).toFixed(1);
197 | return mmolBG;
198 | }
199 |
200 | // converts mmoL to mg/dL
201 | function mgdl( bg ) {
202 | let mgdlBG =(Math.round(bg * 18.018).toFixed(0));
203 | return mgdlBG;
204 | }
205 |
--------------------------------------------------------------------------------
/website/src/assets/sad.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/website/src/pages/Customize.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Customize Glance
5 |
6 | Glance has a very dynamic set of settings allowing it to be used in a
7 | variety of different use cases. below are the settings that you can
8 | change to your liking.
9 |
10 |
Settings glossary/key
11 |
251 |
252 |
253 |
254 |
255 |
262 |
263 |
--------------------------------------------------------------------------------
/resources/index.view:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 99
8 | --
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | --
23 |
24 |
25 |
26 |
27 |
28 |
29 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
184 |
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/website/src/assets/quasar-logo-full.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
43 |
45 |
46 |
48 | image/svg+xml
49 |
51 |
52 |
53 |
54 |
55 |
60 |
63 |
66 |
69 |
75 |
79 |
83 |
87 |
91 |
95 |
99 |
103 |
104 |
105 |
106 |
107 |
113 |
118 |
126 |
133 |
142 |
151 |
160 |
169 |
178 |
187 |
188 |
189 |
190 |
191 |
192 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2018 Ryan Mason - All Rights Reserved
3 | *
4 | * Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.
5 | *
6 | * https://github.com/Rytiggy/Glance/blob/master/LICENSE
7 | * ------------------------------------------------
8 | *
9 | * You are free to modify the code but please leave the copyright in the header.
10 | *
11 | * ------------------------------------------------
12 | */
13 |
14 | import document from "document";
15 | import { inbox } from "file-transfer";
16 | import fs from "fs";
17 | import { vibration } from "haptics";
18 | import DateTime from "../modules/app/dateTime.js";
19 | import BatteryLevels from "../modules/app/batteryLevels.js";
20 | import Graph from "../modules/app/bloodline.js";
21 | import UserActivity from "../modules/app/userActivity.js";
22 | import Alerts from "../modules/app/alerts.js";
23 | import Errors from "../modules/app/errors.js";
24 | import Transfer from "../modules/app/transfer.js";
25 | // import { preferences, save, load } from "../modules/app/sharedPreferences";
26 | import { memory } from "system";
27 |
28 | const dateTime = new DateTime();
29 | const batteryLevels = new BatteryLevels();
30 | const graph = new Graph();
31 | const userActivity = new UserActivity();
32 | const alerts = new Alerts();
33 | const errors = new Errors();
34 | const transfer = new Transfer();
35 |
36 | let main = document.getElementById("main");
37 | let sgv = document.getElementById("sgv");
38 | let rawbg = document.getElementById("rawbg");
39 | let tempBasal = document.getElementById("tempBasal");
40 | let largeGraphsSgv = document.getElementById("largeGraphsSgv");
41 | let delta = document.getElementById("delta");
42 | let largeGraphDelta = document.getElementById("largeGraphDelta");
43 | let timeOfLastSgv = document.getElementById("timeOfLastSgv");
44 | let largeGraphTimeOfLastSgv = document.getElementById(
45 | "largeGraphTimeOfLastSgv"
46 | );
47 | let largeGraphIob = document.getElementById("largeGraphIob");
48 | let largeGraphCob = document.getElementById("largeGraphCob");
49 | let iob = document.getElementById("iob");
50 | let cob = document.getElementById("cob");
51 |
52 | let dateElement = document.getElementById("date");
53 | let timeElement = document.getElementById("time");
54 | let largeGraphTime = document.getElementById("largeGraphTime");
55 | let weather = document.getElementById("weather");
56 | let arrows = document.getElementById("arrows");
57 | let largeGraphArrows = document.getElementById("largeGraphArrows");
58 | let alertArrows = document.getElementById("alertArrows");
59 | let batteryLevel = document.getElementById("battery-level");
60 | let steps = document.getElementById("steps");
61 | let stepIcon = document.getElementById("stepIcon");
62 | let heart = document.getElementById("heart");
63 | let heartIcon = document.getElementById("heartIcon");
64 | let bgColor = document.getElementById("bgColor");
65 | let largeGraphBgColor = document.getElementById("largeGraphBgColor");
66 | let batteryPercent = document.getElementById("batteryPercent");
67 | let popup = document.getElementById("popup");
68 | let dismiss = popup.getElementById("dismiss");
69 | let errorText = document.getElementById("error");
70 | let popupTitle = document.getElementById("popup-title");
71 | let degreeIcon = document.getElementById("degreeIcon");
72 | let goToLargeGraph = document.getElementById("goToLargeGraph");
73 |
74 | let largeGraphLoopStatus = document.getElementById("largeGraphLoopStatus");
75 | let largeGraphView = document.getElementById("largeGraphView");
76 | let exitLargeGraph = document.getElementById("exitLargeGraph");
77 |
78 | let largeGraphSyringe = document.getElementById("largeGraphSyringe");
79 | let largeGraphHamburger = document.getElementById("largeGraphHamburger");
80 | let syringe = document.getElementById("syringe");
81 | let hamburger = document.getElementById("hamburger");
82 | let predictedBg = document.getElementById("predictedBg");
83 |
84 | let dismissHighFor = 120;
85 | let dismissLowFor = 15;
86 |
87 | let data = null;
88 | let DISABLE_ALERTS = false;
89 |
90 | // Data to send back to phone
91 | let dataToSend = {
92 | heart: 0,
93 | steps: userActivity.get().steps,
94 | };
95 | dismiss.onclick = function (evt) {
96 | console.log("DISMISS");
97 | popup.style.display = "none";
98 | popupTitle.style.display = "none";
99 | vibration.stop();
100 | DISABLE_ALERTS = true;
101 | let currentBgFromBloodSugars = getFistBgNonpredictiveBG(data.bloodSugars.bgs);
102 |
103 | if (currentBgFromBloodSugars.sgv >= parseInt(data.settings.highThreshold)) {
104 | console.log("HIGH " + dismissHighFor);
105 | setTimeout(disableAlertsFalse, dismissHighFor * 1000 * 60);
106 | } else {
107 | // 15 mins
108 | console.log("LOW " + dismissLowFor);
109 |
110 | setTimeout(disableAlertsFalse, dismissLowFor * 1000 * 60);
111 | }
112 | };
113 |
114 | function disableAlertsFalse() {
115 | DISABLE_ALERTS = false;
116 | }
117 |
118 | sgv.text = "---";
119 | rawbg.text = "";
120 | delta.text = "";
121 | largeGraphDelta.text = "";
122 | iob.text = "0.0";
123 | cob.text = "0.0";
124 | largeGraphIob.text = "0.0";
125 | largeGraphCob.text = "0.0";
126 | dateElement.text = "";
127 | timeOfLastSgv.text = "";
128 | weather.text = "--";
129 | steps.text = "--";
130 | heart.text = "--";
131 | batteryPercent.text = "%";
132 | bgColor.gradient.colors.c1 = "#390263";
133 | largeGraphBgColor.gradient.colors.c1 = "#390263";
134 | errorText.text = "";
135 | update();
136 | setInterval(update, 10000);
137 |
138 | timeElement.text = dateTime.getTime();
139 | largeGraphTime.text = dateTime.getTime();
140 | batteryLevel.width = batteryLevels.get().level;
141 |
142 | inbox.onnewfile = () => {
143 | console.log("New file!");
144 | let fileName;
145 | do {
146 | // If there is a file, move it from staging into the application folder
147 | fileName = inbox.nextFile();
148 | if (fileName) {
149 | data = fs.readFileSync(fileName, "cbor");
150 | update();
151 | }
152 | } while (fileName);
153 | };
154 |
155 | function update() {
156 | console.log("app - update()");
157 | console.warn("JS memory: " + memory.js.used + "/" + memory.js.total);
158 | let heartrate = userActivity.get().heartRate;
159 | if (!heartrate) {
160 | heartrate = 0;
161 | }
162 | // Data to send back to phone
163 | dataToSend = {
164 | heart: heartrate,
165 | steps: userActivity.get().steps,
166 | };
167 |
168 | if (data) {
169 | console.warn("GOT DATA");
170 | batteryLevel.width = batteryLevels.get().level;
171 | batteryLevel.style.fill = batteryLevels.get().color;
172 | batteryPercent.text = "" + batteryLevels.get().percent + "%";
173 | timeElement.text = dateTime.getTime(data.settings.timeFormat);
174 | largeGraphTime.text = dateTime.getTime(data.settings.timeFormat);
175 |
176 | dismissHighFor = data.settings.dismissHighFor;
177 | dismissLowFor = data.settings.dismissLowFor;
178 | weather.text = ""; // data.weather.temp;
179 | degreeIcon.style.display = "none";
180 |
181 | // colors
182 | bgColor.gradient.colors.c1 = data.settings.bgColor;
183 | bgColor.gradient.colors.c2 = data.settings.bgColorTwo;
184 |
185 | largeGraphBgColor.gradient.colors.c1 = data.settings.bgColor;
186 | largeGraphBgColor.gradient.colors.c2 = data.settings.bgColorTwo;
187 |
188 | setTextColor(data.settings.textColor);
189 | // bloodsugars
190 | let currentBgFromBloodSugars = getFistBgNonpredictiveBG(
191 | data.bloodSugars.bgs
192 | );
193 |
194 | // Layout options
195 | if (
196 | currentBgFromBloodSugars[data.settings.layoutOne] &&
197 | data.settings.layoutOne != "iob"
198 | ) {
199 | iob.text = currentBgFromBloodSugars[data.settings.layoutOne];
200 | syringe.style.display = "none";
201 | iob.x = 10;
202 | } else {
203 | iob.text = commas(userActivity.get().steps);
204 | syringe.style.display = "inline";
205 | iob.x = 35;
206 | if (currentBgFromBloodSugars.iob && currentBgFromBloodSugars.iob != 0) {
207 | iob.text = currentBgFromBloodSugars.iob + "";
208 | largeGraphIob.text = currentBgFromBloodSugars.iob + "";
209 | syringe.style.display = "inline";
210 | largeGraphSyringe.style.display = "inline";
211 | } else {
212 | iob.text = "";
213 | largeGraphIob.text = "";
214 | syringe.style.display = "none";
215 | largeGraphSyringe.style.display = "none";
216 | }
217 | }
218 |
219 | if (
220 | currentBgFromBloodSugars[data.settings.layoutTwo] &&
221 | data.settings.layoutTwo != "cob"
222 | ) {
223 | cob.text = currentBgFromBloodSugars[data.settings.layoutTwo];
224 | hamburger.style.display = "none";
225 | cob.x = 10;
226 | } else {
227 | cob.text = userActivity.get().heartRate;
228 | hamburger.style.display = "inline";
229 | cob.x = 35;
230 | if (currentBgFromBloodSugars.cob && currentBgFromBloodSugars.cob != 0) {
231 | cob.text = currentBgFromBloodSugars.cob + "";
232 | largeGraphCob.text = currentBgFromBloodSugars.cob + "";
233 | hamburger.style.display = "inline";
234 | largeGraphHamburger.style.display = "inline";
235 | } else {
236 | cob.text = "";
237 | largeGraphCob.text = "";
238 | hamburger.style.display = "none";
239 | largeGraphHamburger.style.display = "none";
240 | }
241 | }
242 |
243 | if (
244 | currentBgFromBloodSugars[data.settings.layoutThree] &&
245 | data.settings.layoutThree != "steps"
246 | ) {
247 | steps.text = currentBgFromBloodSugars[data.settings.layoutThree];
248 | stepIcon.style.display = "none";
249 | steps.x = 10;
250 | } else {
251 | steps.text = commas(userActivity.get().steps);
252 | stepIcon.style.display = "inline";
253 | steps.x = 35;
254 | }
255 |
256 | if (
257 | currentBgFromBloodSugars[data.settings.layoutFour] &&
258 | data.settings.layoutFour != "heart"
259 | ) {
260 | heart.text = currentBgFromBloodSugars[data.settings.layoutFour];
261 | heartIcon.style.display = "none";
262 | heart.x = 10;
263 | } else {
264 | heart.text = userActivity.get().heartRate;
265 | heartIcon.style.display = "inline";
266 | heart.x = 35;
267 | }
268 |
269 | sgv.text = currentBgFromBloodSugars.currentbg;
270 | largeGraphsSgv.text = currentBgFromBloodSugars.currentbg;
271 | if (currentBgFromBloodSugars.rawbg) {
272 | rawbg.text = currentBgFromBloodSugars.rawbg + " ";
273 | } else {
274 | rawbg.text = "";
275 | }
276 |
277 | if (currentBgFromBloodSugars.tempbasal) {
278 | tempBasal.text = currentBgFromBloodSugars.tempbasal;
279 | } else {
280 | tempBasal.text = "";
281 | }
282 |
283 | if (currentBgFromBloodSugars.predictedbg) {
284 | predictedBg.text = currentBgFromBloodSugars.predictedbg;
285 | } else {
286 | predictedBg.text = "";
287 | }
288 |
289 | timeOfLastSgv.text = dateTime.getTimeSenseLastSGV(
290 | currentBgFromBloodSugars.datetime
291 | )[0];
292 | largeGraphTimeOfLastSgv.text = dateTime.getTimeSenseLastSGV(
293 | currentBgFromBloodSugars.datetime
294 | )[0];
295 |
296 | dateElement.text = dateTime.getDate(
297 | data.settings.dateFormat,
298 | data.settings.enableDOW
299 | );
300 |
301 | let timeSenseLastSGV = dateTime.getTimeSenseLastSGV(
302 | currentBgFromBloodSugars.datetime
303 | )[1];
304 | // if DISABLE_ALERTS is true check if user is in range
305 | if (DISABLE_ALERTS && data.settings.resetAlertDismissal) {
306 | if (
307 | parseInt(timeSenseLastSGV, 10) < data.settings.staleDataAlertAfter &&
308 | currentBgFromBloodSugars.direction != "DoubleDown" &&
309 | currentBgFromBloodSugars.direction != "DoubleUp" &&
310 | currentBgFromBloodSugars.loopstatus != "Warning"
311 | ) {
312 | // Dont reset alerts for LOS, DoubleUp, doubleDown, Warning
313 | if (
314 | currentBgFromBloodSugars.sgv > parseInt(data.settings.lowThreshold) &&
315 | currentBgFromBloodSugars.sgv < parseInt(data.settings.highThreshold)
316 | ) {
317 | // if the BG is between the threshold
318 | console.error("here", DISABLE_ALERTS, parseInt(timeSenseLastSGV, 10));
319 | disableAlertsFalse();
320 | }
321 | }
322 | }
323 |
324 | alerts.check(
325 | currentBgFromBloodSugars,
326 | data.settings,
327 | DISABLE_ALERTS,
328 | timeSenseLastSGV
329 | );
330 |
331 | errors.check(timeSenseLastSGV, currentBgFromBloodSugars.currentbg);
332 | let deltaText = currentBgFromBloodSugars.bgdelta;
333 | // add Plus
334 | if (deltaText > 0) {
335 | deltaText = "+" + deltaText;
336 | }
337 | delta.text = deltaText + " " + data.settings.glucoseUnits;
338 | largeGraphDelta.text = deltaText + " " + data.settings.glucoseUnits;
339 | largeGraphLoopStatus.text = ""; // currentBgFromBloodSugars.loopstatus;
340 |
341 | arrows.href =
342 | "../resources/img/arrows/" + currentBgFromBloodSugars.direction + ".png";
343 | largeGraphArrows.href =
344 | "../resources/img/arrows/" + currentBgFromBloodSugars.direction + ".png";
345 |
346 | graph.update(
347 | data.bloodSugars.bgs,
348 | data.settings.highThreshold,
349 | data.settings.lowThreshold,
350 | data.settings
351 | );
352 |
353 | if (data.settings.largeGraph) {
354 | goToLargeGraph.style.display = "inline";
355 | } else {
356 | goToLargeGraph.style.display = "none";
357 | }
358 | // if (data.settings.treatments) {
359 | // goToTreatment.style.display = "inline";
360 | // } else {
361 | // goToTreatment.style.display = "none";
362 | // }
363 | } else {
364 | console.warn("NO DATA");
365 | steps.text = commas(userActivity.get().steps);
366 | heart.text = userActivity.get().heartRate;
367 | batteryLevel.width = batteryLevels.get().level;
368 | batteryPercent.text = "" + batteryLevels.get().percent + "%";
369 |
370 | timeElement.text = dateTime.getTime();
371 | largeGraphTime.text = dateTime.getTime();
372 |
373 | dateElement.text = dateTime.getDate();
374 | }
375 | }
376 |
377 | function commas(value) {
378 | return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
379 | }
380 | /**
381 | * Get Fist BG that is not a predictive BG
382 | * @param {Array} bgs
383 | * @returns {Array}
384 | */
385 | function getFistBgNonpredictiveBG(bgs) {
386 | return bgs.filter((bg) => {
387 | if (bg.bgdelta || bg.bgdelta === 0) {
388 | return true;
389 | }
390 | })[0];
391 | }
392 |
393 | function setTextColor(color) {
394 | let domElemets = [
395 | "iob",
396 | "cob",
397 | "heart",
398 | "steps",
399 | "batteryPercent",
400 | "date",
401 | "delta",
402 | "timeOfLastSgv",
403 | "time",
404 | "high",
405 | "low",
406 | "largeGraphHigh",
407 | "largeGraphLow",
408 | "largeGraphDelta",
409 | "largeGraphTimeOfLastSgv",
410 | "largeGraphIob",
411 | "largeGraphCob",
412 | "predictedBg",
413 | "largeGraphTime",
414 | "largeGraphLoopStatus",
415 | "tempBasal",
416 | ];
417 | domElemets.forEach((ele) => {
418 | document.getElementById(ele).style.fill = color;
419 | });
420 | }
421 |
422 | goToLargeGraph.onclick = (e) => {
423 | console.log("goToLargeGraph Activated!");
424 | vibration.start("bump");
425 | largeGraphView.style.display = "inline";
426 | main.style.display = "none";
427 | };
428 |
429 | exitLargeGraph.onclick = (e) => {
430 | console.log("exitLargeGraph Activated!");
431 | vibration.start("bump");
432 | largeGraphView.style.display = "none";
433 | main.style.display = "inline";
434 | };
435 |
436 | timeElement.onclick = (e) => {
437 | console.log("FORCE Activated!");
438 | transfer.send(dataToSend);
439 | vibration.start("bump");
440 | arrows.href = "../resources/img/arrows/loading.png";
441 | largeGraphArrows.href = "../resources/img/arrows/loading.png";
442 | alertArrows.href = "../resources/img/arrows/loading.png";
443 | };
444 |
445 | // wait 2 seconds
446 | setTimeout(function () {
447 | transfer.send(dataToSend);
448 | }, 1500);
449 | setInterval(function () {
450 | transfer.send(dataToSend);
451 | }, 180000);
452 |
453 | //
454 |
--------------------------------------------------------------------------------
/settings/index.jsx:
--------------------------------------------------------------------------------
1 | import { settings } from "settings";
2 | import { XMLHttpRequest } from "xmlhttprequest";
3 |
4 | function mySettings(props) {
5 | return (
6 |
7 |
8 |
13 |
14 |
15 |
16 | Glance is a solution for use with Fitbit devices to view your blood
17 | glucose levels along with a variety of other health stats on the watch
18 | face. You can see your stats at a glance!
19 |
20 |
21 |
22 |
23 | Click here to learn how to set up Glance!
24 |
25 |
26 |
27 |
28 |
31 | Data Source Settings
32 |
33 | }
34 | >
35 |
47 |
48 | {props.settings.dataSource ? (
49 | JSON.parse(props.settings.dataSource).values[0].value == "custom" ? (
50 |
51 | ) : null
52 | ) : null}
53 |
54 | {props.settings.dataSource ? (
55 | JSON.parse(props.settings.dataSource).values[0].value ==
56 | "nightscout" ? (
57 |
58 | https://SiteName.NightscoutHostSite .com
59 |
60 | ) : null
61 | ) : null}
62 | {props.settings.dataSource ? (
63 | JSON.parse(props.settings.dataSource).values[0].value ==
64 | "nightscout" ? (
65 |
74 | ) : null
75 | ) : null}
76 |
77 | {props.settings.dataSource ? (
78 | JSON.parse(props.settings.dataSource).values[0].value ==
79 | "nightscout" ? ( (JSON.parse(props.settings.nightscoutSiteHost).values[0].value == "" ? (
80 |
81 | https://FullCustomDomain.com
82 |
83 | ) : (
84 |
85 | https://SiteName .NightscoutHostSite.com
86 |
87 | )
88 | )
89 | ) : null
90 | ) : null}
91 | {props.settings.dataSource ? (
92 | JSON.parse(props.settings.dataSource).values[0].value ==
93 | "nightscout" ? (
94 |
99 | ) : null
100 | ) : null}
101 |
102 | {props.settings.dataSource ? (
103 | JSON.parse(props.settings.dataSource).values[0].value ==
104 | "nightscout" ? (
105 |
110 | ) : null
111 | ) : null}
112 |
113 | {props.settings.dataSource ? (
114 | JSON.parse(props.settings.dataSource).values[0].value == "dexcom" ? (
115 |
118 | Dexcom
119 |
120 | }
121 | >
122 |
123 | Dexcom
124 |
125 |
130 |
135 |
139 |
140 | ) : null
141 | ) : null}
142 |
143 |
144 |
147 | Glucose Settings
148 |
149 | }
150 | >
151 |
159 |
160 |
161 |
162 |
166 |
167 | {props.settings.extraGlucoseSettings ? (
168 | JSON.parse(props.settings.extraGlucoseSettings) == true ? (
169 |
172 | Extra Glucose Settings
173 |
174 | }
175 | >
176 |
177 | Alerts
178 |
179 |
180 |
184 |
185 |
189 |
190 |
191 | {props.settings.dataSource ? (
192 | JSON.parse(props.settings.dataSource).values[0].value ==
193 | "nightscout" ? (
194 |
195 | ) : null
196 | ) : null}
197 |
198 |
202 |
206 |
207 | ) : null
208 | ) : null}
209 |
210 |
211 |
214 | Date/Time Settings
215 |
216 | }
217 | >
218 |
226 |
236 |
237 |
238 |
239 |
242 | Layout
243 |
244 | }
245 | >
246 |
247 | Graph
248 |
249 | {props.settings.dataSource ? (
250 | JSON.parse(props.settings.dataSource).values[0].value ==
251 | "nightscout" ? (
252 |
256 | ) : null
257 | ) : null}
258 |
259 |
260 | Tap the lower right hand side of the watch faces screen to view the
261 | larger graph popup screen.
262 |
263 |
264 |
265 | Background Color
266 |
267 |
268 |
280 |
281 | {props.settings.bgColor ? (
282 | JSON.parse(props.settings.bgColor) == "#FFFFFF" ? (
283 |
284 |
285 | Random Color Generator
286 |
287 |
288 | The white color circle will generate a random color for you, if
289 | you find a color that youbold like turn on save color to save
290 | it! Need help finding a hex color code?{" "}
291 |
292 | check out this site.
293 |
294 |
295 |
296 | {" "}
297 | {" "}
298 |
299 |
300 | ) : null
301 | ) : null}
302 |
303 | {props.settings.dataSource ? (
304 | JSON.parse(props.settings.dataSource).values[0].value ==
305 | "nightscout" ||
306 | JSON.parse(props.settings.dataSource).values[0].value == "spike" ? (
307 |
308 |
309 | Customize
310 |
311 |
312 | The customize section is used for customizing the user interface
313 | of Glance, you can replace the default values of Glance with
314 | other values present.
315 |
316 |
317 | Note: If the value selected is not present on your data source
318 | it will show the default option.
319 |
320 |
334 |
348 |
362 |
376 |
377 | ) : null
378 | ) : null}
379 |
380 |
383 | Helpful links
384 |
385 | }
386 | >
387 |
388 | If you need help getting started with Glance follow the links below!
389 |
390 |
391 | How to set up Glance
392 |
393 |
394 | Note: Tapping on the time should try to force the watch to sync.
395 | You'll feel the watch vibrate.
396 |
397 |
398 |
399 |
400 | {props.settings.toggle ? (
401 | JSON.parse(props.settings.toggle) == true ? (
402 |
405 | Logs
406 |
407 | }
408 | >
409 |
410 |
414 | props.settingsStorage.setItem(
415 | "logs",
416 | JSON.stringify({ name: "Resyncing" })
417 | )
418 | }
419 | />
420 | props.settingsStorage.clear()}
424 | />
425 |
426 | ) : null
427 | ) : null}
428 |
429 | );
430 | }
431 |
432 | registerSettingsPage(mySettings);
433 |
434 | //
435 | // Weather
436 | //
437 | //
438 |
439 | // Treatment
440 | // {((props.settings.dataSource) ? ((JSON.parse(props.settings.dataSource).values[0].value == 'xdrip') ?
441 | // xDrip does not support treatments through API calls. maybe in the future it will! : null) : null)}
442 | // {((props.settings.dataSource) ? ((JSON.parse(props.settings.dataSource).values[0].value != 'xdrip') ?
443 | // : null) : null)}
444 | // Tap the lower right hand side of the watch faces screen to enter treatment info.
445 | // {((props.settings.dataSource) ? ((JSON.parse(props.settings.dataSource).values[0].value == 'nightscout') ?
446 | // : null) : null)}
447 |
--------------------------------------------------------------------------------