# Project export: Clock In

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 10.0
- Tagline: A Dash Cam for the body, keeping workers safe from hazardous work conditions.
- Devpost: https://devpost.com/software/clock-in
- GitHub: https://github.com/y-dejong/clock-in
- Video: https://www.youtube.com/embed/rgkBGJMnWxU?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Best Use of Zepp Health)
- Team: 2 GitHub contributor(s) — Gregory Shklovski (6 commits), Yasper De Jong (5 commits)

## Devpost submission (written by the team)

### Inspiration

Smart watches have centered around the health of their users for over a decade. Fitness apps have provided the ability to track calorie burning and heart rate for athletes, and those pursuing the ideal physique. At the other end of the spectrum, medical apps provide the ability for diabetes patients and the elderly to track and detect concerning medical phenomena. However, both these use cases miss the major demographic between them: middle class and blue collar workers. For construction workers, contractors, farmers, and millions of other manual laborers, the extreme highs and lows of biological tracking are not necessary, yet these apps are incapable of keeping them safe and healthy in their physically demanding workplaces. Clock In provides an answer.

### What it does

The moment a worker "Clocks In" at the start of their day, they can open our app on their watch to start tracking them. Clock In tracks several health and safety risk factors throughout the work day, and detects any major hazards or accidents that could affect the user. It tracks personal factors, such as heart rate, blood oxygen level, physical trauma via an accelerometer, atrial fibrillation, and arrhythmia. It also tracks environmental factors, such as temperature and humidity. Typically, Clock In will invalidate and delete this data after a few hours, but the moment that a workplace hazard or accident strikes, Clock In takes a snapshot of the user's working conditions at the time of the incident and in the hours leading up to it. In this way, Clock In acts like a dash cam for the body, freezing the data surrounding an incident for later investigation. Clock In provides workers with the ability to record unsafe or inhumane working conditions, providing numerical, indisputable evidence in the case of mistreatment by their employer. In the case of a severe accident, this data can provide valuable proof of mistreatment by an employer, assisting the worker's case.

### How we built it

Using Zepp's OS and the vast array of sensors on the Amazfit Smart-Watch, we monitor and protect against 3 kinds of trigger events. immediate triggers High-speed impact Loud Noise Self-Recorded Emergency short term triggers Abnormal Heart Rate Low VO2 max long term triggers Unsafe temperatures Unsafe altitudes

### Challenges we ran into

Use of an unfamiliar API with mixed documentation and a foreign framework. Luckily, the Zepp dev team gave us 24-hour support, constantly helping with documentation and hardware issues.

### Accomplishments we're proud of

Our technology will improve safety accountability in the workplace by ensuring all sides are held accountable for workplace mishaps. We are proud of the high-speed impact detection. A user can unnaturally jerk their arm and the watch immediately responds. We are also proud of our ability complete a project despite arriving late to the hackathon and having other obligations during the event.

### What's next

Workplace injuries no more Suppose an employee was injured. There is little certainty that their medical bills will be We hope to implement preventative measures to stop workplace injuries before they happen. By notifying workers when they are susceptible to stroke, drowsiness, and immune system failure we can prevent a hospital visit that is both expensive to the employee and the employer. In the future, we hope to add more trackable factors to our product, such as air quality and elevation.

## README (from the GitHub repository)

# clock-in
Track working conditions

## 🏆Calhacks 10.0 Winner: Best Zepp Health App🏆
https://devpost.com/software/clock-in


## Detected evidence (automated analysis)

Indexed codebase: 11 recognized source files, 15 KB.
- JavaScript (language) — detected in the code
- TypeScript (language) — detected in the code

## Codebase structure (from repository index)

### Files (19 of 19)

```
.DS_Store
.gitignore
app-side/index.js
app.js
app.json
assets/.DS_Store
global.d.ts
jsconfig.json
package.json
page/.DS_Store
page/gt/home/index.page.js
page/gt/home/index.page.r.layout.js
page/gt/home/index.page.s.layout.js
page/gt/record/index.page.js
page/gt/stop/index.page.js
page/gt/trigger/index.page.js
page/i18n/en-US.po
README.md
utils/index.js
```

### Dependencies

- package.json: @zeppos/device-types@^3.0.0, @zeppos/zml@^0.0.9

### Recent commits (newest first)

- README.md edits
- Corrected report
- Added reports
- Messages showing on trigger!!!!
- Merge branch 'main' of https://github.com/y-dejong/clock-in
- Trigger page 80%
- Added sensors
- Added relevant permissions
- Recording page ui completed
- Added recording page and started trigger page
- Created first page UI with button
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "helloworld",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@zeppos/device-types": "^3.0.0"
  },
  "dependencies": {
    "@zeppos/zml": "^0.0.9"
  }
}

```

### app.js

```javascript
import { log as Logger } from "@zos/utils";

const logger = Logger.getLogger("clock-in");
const filename = "status_report.txt";

App({
  globalData: {
    recording: false
  },
  onCreate(options) {
    console.log("app on create invoke");
  },

  onDestroy(options) {
    console.log("app on destroy invoke");
  },
});

```

### utils/index.js

```javascript
export function assets(type) {
  return (path) => type + "/" + path;
}

```

### app-side/index.js

```javascript
async function fetchTemperature(res) {
  const data = await fetch("https://api.openweathermap.org/data/2.5/weather?lat=37.78&lon=-122.42&appid=f56f2a9ee78fd7268c6b4618b04c31be")
  const datajson = await data.json();
  console.log("Got temp");
  console.log(JSON.stringify(data));
  res(null, {
    result: datajson.main.temp
  });
}

AppSideService({
  onInit() {
    
  },

  onRun() {
    
  },

  onRequest(req, res) {
    console.log("=====>", req.method);
    if(req.method == "GET_DATA") {
      fetchData(res);
    }
  },

  onDestroy() {
    
  }
});

```

### global.d.ts

```typescript
/// <reference path="node_modules/@zeppos/device-types/dist/index.d.ts" />

```

### page/gt/home/index.page.r.layout.js

```javascript
import * as hmUI from "@zos/ui";
import { getText } from "@zos/i18n";
import { getDeviceInfo } from "@zos/device";
import { px } from "@zos/utils";

export const { width: DEVICE_WIDTH, height: DEVICE_HEIGHT } = getDeviceInfo();

export const TEXT_STYLE = {
  text: getText("appName"),
  x: px(42),
  y: px(200),
  w: DEVICE_WIDTH - px(42) * 2,
  h: px(100),
  color: 0xffffff,
  text_size: px(36),
  align_h: hmUI.align.CENTER_H,
  align_v: hmUI.align.CENTER_V,
  text_style: hmUI.text_style.WRAP,
};

```

### page/gt/home/index.page.s.layout.js

```javascript
import * as hmUI from "@zos/ui";
import { getText } from "@zos/i18n";
import { getDeviceInfo } from "@zos/device";
import { px } from "@zos/utils";

export const { width: DEVICE_WIDTH, height: DEVICE_HEIGHT } = getDeviceInfo();

export const TEXT_STYLE = {
  text: getText("appName"),
  x: px(42),
  y: px(200),
  w: DEVICE_WIDTH - px(42) * 2,
  h: px(100),
  color: 0xffffff,
  text_size: px(36),
  align_h: hmUI.align.CENTER_H,
  align_v: hmUI.align.CENTER_V,
  text_style: hmUI.text_style.WRAP,
};

```

### page/gt/home/index.page.js

```javascript
import { createWidget, widget, align, prop, text_style, event } from "@zos/ui";
import { log as Logger } from "@zos/utils";
import { push } from "@zos/router";
import { TEXT_STYLE } from "zosLoader:./index.page.[pf].layout.js";

const logger = Logger.getLogger("Hello World");
Page({
  onInit() {
    logger.debug("page onInit invoked");
  },
  build() {
    logger.debug("page build invoked");
    //let count = 0;
    const text = createWidget(widget.TEXT, {
      x: 96,
      y: 120,
      w: 288,
      h: 46,
      color: 0xffffff,
      text_size: 36,
      align_h: align.CENTER_H,
      align_v: align.CENTER_V,
      text_style: text_style.NONE,
      text: "Clock In",
    });

    const button = createWidget(widget.BUTTON, {
      x: 185,
      y: 332,
      w: 110,
      h: 110,
      radius: 25,
      normal_src: "clockin_icon.png", // color 17
      press_src: "clockin_icon_pressed.png", // color 18
      text: "",
      click_func: (button_widget) => {
        logger.log("Clicked button, starting recording");
        push({
          url: "page/gt/record/index.page",
          // params: "",  can pass in a json object
        });
      },
    });

    /*
    text.addEventListener(event.CLICK_DOWN, (info) => {
      text.setProperty(prop.MORE, {});
      count = count + 1;
      logger.debug(`Text count: ${count}`);
    });
    
    ^ example of adding an event listener to an element
    */
  },

  onDestroy() {
    logger.debug("page onDestroy invoked");
  },
});

```

### page/gt/stop/index.page.js

```javascript
import { log as Logger } from "@zos/utils";
import {
  createWidget,
  widget,
  align,
  prop,
  text_style,
  event,
  anim_status,
} from "@zos/ui";
const logger = Logger.getLogger("Hello World");
import { push, back } from "@zos/router";

const scrollBar = createWidget(widget.PAGE_SCROLLBAR);

function generateReport(healthdata) {
  var result = "During your workday:\n";
  var totalhr = 0;
  healthdata.hr_history.forEach((x) => totalhr += x);
  result += "Your average heart rate was " +
    (((totalhr / healthdata.hr_history.length) > 140) ?
     "elevated" : "normal");
  result += " (avg: " + (totalhr/healthdata.hr_history.length) + ").\n";

  var totalspo2 = 0;
  healthdata.spo2_history.forEach((x) => totalspo2 += x);
  result += "Your average blood oxygen level was " +
    (((totalspo2 / healthdata.spo2_history.length) < 90) ?
     "too low" : "normal");
  result += " (avg: " + (totalspo2/healthdata.spo2_history.length) + ").";

  result += "\n\nHazards during day:\n";
  result += `${healthdata.incident.hour}:${("0" + healthdata.incident.minute).slice(-2)}: ${healthdata.incident.description}`;

  return result;
}

Page({
  onInit(params) {
    var paramobj = JSON.parse(params);
    var trigger_description = paramobj.description;
    const title = createWidget(widget.TEXT, {
      x: 0,
      y: 20,
      w: 480,
      h: 80,
      color: 0xffffff,
      text_size: 36,
      align_h: align.CENTER_H,
      align_v: align.CENTER_V,
      text_style: text_style.WRAP,
      text: "Report",
    });

    const text = createWidget(widget.TEXT, {
      x: 0,
      y: 120,
      w: 480,
      h: 1000,
      color: 0xffffff,
      text_size: 36,
      align_h: align.CENTER_H,
      align_v: align.TOP,
      text_style: text_style.WRAP,
      text: generateReport(healthdata),
    });
    // logger.debug("stop page onInit invoked" + params.description);
  },
  build() {
    logger.debug("stop page build invoked");
  },

  onDestroy() {
    logger.debug("stop page onDestroy invoked");
  },
});

```

### page/gt/trigger/index.page.js

```javascript
import { log as Logger } from "@zos/utils";
import {
  createWidget,
  widget,
  align,
  prop,
  text_style,
  event,
  anim_status,
} from "@zos/ui";
import { Time } from "@zos/sensor";
const logger = Logger.getLogger("Hello World");
import { push, back } from "@zos/router";

const time = new Time();

var trigger_description;
var healthdata;
Page({
  onInit(params) {
    logger.debug("printing params");
    logger.debug(params);
    const paramobj = JSON.parse(params);
    trigger_description = paramobj.description;
    logger.debug(
      "trigger page onInit invoked for the following reason: " +
        trigger_description
    );
    healthdata = paramobj.healthdata;
    logger.debug(JSON.stringify(healthdata));

    healthdata.incident = {
      hour: time.getHours(),
      minute: time.getMinutes(),
      description: trigger_description
    };
  },
  build() {
    logger.debug("trigger page build invoked");

    // const hazard_title = createWidget(widget.TEXT, {
    //   x: 60,
    //   y: 80,
    //   h: 80,
    //   w: 320,
    //   color: 0xffffff,
    //   text_size: 36,
    //   align_h: align.CENTER_H,
    //   align_v: align.CENTER_V,
    //   text_style: text_style.NONE,
    //   text: "Hazard Detected",
    // });
    logger.debug("trigger description in build: " + trigger_description);

    const hazard_text = createWidget(widget.TEXT, {
      x: 0,
      y: 80,
      h: 160,
      w: 480,
      color: 0xffffff,
      text_size: 36,
      align_h: align.CENTER_H,
      align_v: align.CENTER_V,
      text_style: text_style.NONE,
      text: trigger_description,
    });

    const hazard_icon = createWidget(widget.IMG, {
      x: 184,
      y: 200,
      w: 112,
      src: "warning.png",
    });

    // const animated_timer_bar = createWidget(widget.FILL_RECT, {
    //   x: 150,
    //   y: 132,
    //   w: 180,
    //   h: 40,
    //   color: 0xbbbbbb,
    // });

    // const animated_timer_bar_overlay = createWidget(widget.FILL_RECT, {
    //   x: 0,
    //   y: 132,
    //   w: 480,
    //   h: 40,
    //   color: 0x00ff00,
    // });

    // const anim_step1 = {
    //   anim_rate: "linear",
    //   anim_duration: 10,
    //   anim_from: px(0),
    //   anim_to: px(480),
    //   anim_prop: prop.w,
    // };

    // const animId = animated_timer_bar_overlay.setProperty(prop.ANIM, {
    //   anim_steps: [anim_step1],
    //   anim_fps: 25,
    // });

    // animated_timer_bar_overlay.setProperty(prop.ANIM_STATUS, {
    //   anim_id: animId,
    //   anim_status: anim_status.RESUME,
    // });

    // animated_timer_bar_overlay.setProperty(prop.ANIM_STATUS, {
    //   anim_id: animId,
    //   anim_status: anim_status.PAUSE,
    // });

    // const currentStatus = animated_timer_bar_overlay.getProperty(
    //   prop.ANIM_STATUS,
    //   animId
    // );

    const dismiss_button = createWidget(widget.BUTTON, {
      x: 40,
      y: 350,
      w: 180,
      h: 60,
      radius: 5,
      text_size: 18,
      normal_color: 0x888888,
      press_color: 0x333333,
      text: "Dismiss",
      click_func: (button_widget) => {
        logger.log("Clicked button, leaving trigger");
        back();
      },
    });

    const stop_button = createWidget(widget.BUTTON, {
      x: 260,
      y: 350,
      w: 180,
      h: 60,
      text_size: 18,
      normal_color: 0xff0000,
      press_color: 0x00ff00,
      text: "Report Incident",
      click_func: (button_widget) => {
        button_widget.setProperty(prop.MORE);
        logger.log("Clicked button, end recording");
        push({
          url: "page/gt/stop/index.page",
          params: {
            healthdata: healthdata
          }
        });
      },
    });
  },

  onDestroy() {
    logger.debug("trigger page onDestroy invoked");
  },
});

```

[1 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]