# Project export: Zepptchi

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: Track steps, earn points, and customize your digital Tamagotchi-inspired pet. Fitness meets nostalgic fun. Every step enlivens your pocket-sized pal. Walk more, unlock more. Turn health into play!
- Devpost: https://devpost.com/software/zepptchi
- GitHub: https://github.com/MCGitHub15/zepptchi
- Result: winner (Best Use of Zepp Health)
- Team: 1 GitHub contributor(s) — aneeshpradhan2004 (4 commits)

## Devpost submission (written by the team)

### Inspiration

: The inspiration for this project was finding a way to incentivize healthy activity. While the watch shows people data like steps taken and calories burned, that alone doesn't encourage many people to exercise. By making the app, we hope to make exercise into a game that people look forward to doing rather than something they dread.

### What it does

Zepptchi is an app that allows the user to have their own virtual pet that they can take care of, similar to that of a Tamagotchi. The watch tracks the steps that the user takes and rewards them with points depending on how much they walk. With these points, the user can buy food to nourish their pet which incentivizes exercise. Beyond this, they can earn points to customize the appearance of their pet which further promotes healthy habits.

### How we built it

To build this project, we started by setting up the environment on the Huami OS simulator on a Macbook. This allowed us to test the code on a virtual watch before implementing it on a physical one. We used Visual Studio Code to write all of our code.

### Challenges we ran into

One of the main challenges we faced with this project was setting up the environment to test the watch's capabilities. Out of the 4 of us, only one could successfully install it. This was a huge setback for us since we could only write code on one device. This was worsened by the fact that the internet was unreliable so we couldn't collaborate through other means. One other challenge was

### Accomplishments we're proud of

Our group was most proud of solving the issue where we couldn't get an image to display on the watch. We had been trying for a couple of hours to no avail but we finally found out that it was due to the size of the image. We are proud of this because fixing it showed that our work hadn't been for naught and we got to see our creation working right in front of us on a mobile device. On top of this, this is the first hackathon any of us ever attended so we are extremely proud of coming together and creating something potentially life-changing in such a short time.

### What we learned

One thing we learned is how to collaborate on projects with other people, especially when we couldn't all code simultaneously. We learned how to communicate with the one who was coding by asking questions and making observations to get to the right solution. This was much different than we were used to since school assignments typically only have one person writing code for the entire project. We also became fairly well-acquainted with JavaScript as none of us knew how to use it(at least not that well) coming into the hackathon.

### What's next

The next step for Zepptchi is to include a variety of animals/creatures for the user to have as pets along with any customization that might go with it. This is crucial for the longevity of the game since people may no longer feel incentivized to exercise once they obtain the complete collection. Additionally, we can include challenges(such as burning x calories in 3 days) that give specific rewards to the user which can stave off the repetitive nature of walking steps, buying items, walking steps, buying items, and so on. With this app, we aim to gamify a person's well-being so that their future can be one of happiness and health.

## README (from the GitHub repository)

# zepptchi
Where fitness meets nostalgic fun!

zepptchi, a tomogachi based game app on your wrist! Made for CalHacks 10.0 Hackathon project for ZEPP OS created by Cal State East Bay Students Aneesh Pradhan, Brandon Sandoval Sanchez , Nicholas Ferreira, and Nicholas Ferreira (Table 131).

## Inspiration:
The inspiration for this project was finding a way to incentivize healthy activity. While the watch shows people data like steps taken and calories burned, that alone doesn't encourage many people to exercise. By making the app, we hope to make exercise into a game that people look forward to doing rather than something they dread.

## What it does
Zepptchi is an app that allows the user to have their own virtual pet that they can take care of, similar to that of a Tamagotchi. The watch tracks the steps that the user takes and rewards them with points depending on how much they walk. With these points, the user can buy food to nourish their pet which incentivizes exercise. Beyond this, they can earn points to customize the appearance of their pet which further promotes healthy habits.

## How we built it
To build this project, we started by setting up the environment on the Huami OS simulator on a Macbook. This allowed us to test the code on a virtual watch before implementing it on a physical one. We used Visual Studio Code to write all of our code.

## Challenges we ran into
One of the main challenges we faced with this project was setting up the environment to test the watch's capabilities. Out of the 4 of us, only one could successfully install it. This was a huge setback for us since we could only write code on one device. This was worsened by the fact that the internet was unreliable so we couldn't collaborate through other means. One other challenge was

## Accomplishments that we're proud of
Our group was most proud of solving the issue where we couldn't get an image to display on the watch. We had been trying for a couple of hours to no avail but we finally found out that it was due to the size of the image. We are proud of this because fixing it showed that our work hadn't been for naught and we got to see our creation working right in front of us on a mobile device. On top of this, this is the first hackathon any of us ever attended so we are extremely proud of coming together and creating something potentially life-changing in such a short time.

## What we learned
One thing we learned is how to collaborate on projects with other people, especially when we couldn't all code simultaneously. We learned how to communicate with the one who was coding by asking questions and making observations to get to the right solution. This was much different than we were used to since school assignments typically only have one person writing code for the entire project. We also became fairly well-acquainted with JavaScript as none of us knew how to use it(at least not that well) coming into the hackathon.

## What's next for Zepptchi
The next step for Zepptchi is to include a variety of animals/creatures for the user to have as pets along with any customization that might go with it. This is crucial for the longevity of the game since people may no longer feel incentivized to exercise once they obtain the complete collection. Additionally, we can include challenges(such as burning x calories in 3 days) that give specific rewards to the user which can stave off the repetitive nature of walking steps, buying items, walking steps, buying items, and so on. With this app, we aim to gamify a person's well-being so that their future can be one of happiness and health.


## Detected evidence (automated analysis)

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

## Codebase structure (from repository index)

### Files (17 of 17)

```
.gitignore
app.js
app.json
global.d.ts
jsconfig.json
package.json
page/gt/food-list.js
page/gt/food-list.r.layout.js
page/gt/food-list.s.layout.js
page/gt/index.js
page/gt/index.r.layout.js
page/gt/index.s.layout.js
page/i18n/en-US.po
page/i18n/zh-CN.po
README.md
utils/constants.js
utils/storage.js
```

### Dependencies

- package.json: @silver-zepp/autogui@^1.2.9, @zeppos/device-types@^3.0.0, @zeppos/zml@^0.0.9

### Recent commits (newest first)

- Update README.md
- Update README.md
- Create README.md
- Push update
- Final Build
- Co-authored-by: MC <matthew03.carreon@gmail.com>
- Co-authored-by: MC <matthew03.carreon@gmail.com>

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

### package.json

```
{
  "name": "zepptchi",
  "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": {
    "@silver-zepp/autogui": "^1.2.9",
    "@zeppos/zml": "^0.0.9"
  }
}

```

### app.js

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

const logger = Logger.getLogger("zepptchi-app");
const fileName = "zepptchi_data.txt";

App({
  globalData: {
    foodType: "chocolate",
    localStorage: null,
  },
  onCreate() {
    try {
      this.globalData.localStorage = new LocalStorage(fileName);
      const { foodType = "chocolate" } = this.globalData.localStorage.get();
      this.globalData.foodType = foodType;
    } catch (e) {
      logger.log("--->e:", e);
    }
  },

  onDestroy() {
    this.globalData.localStorage.set({
      foodType: getApp()._options.globalData.foodType,
    });
  },
});

```

### page/gt/index.js

```javascript
// Zepptchi's main page 
import * as hmUI from "@zos/ui";
import AutoGUI, { DEVICE_WIDTH } from '@silver-zepp/autogui';
import { assets } from '@zos/utils';
import { push } from "@zos/router";
import { getText } from "@zos/i18n";
import { Step } from "@zos/sensor";
import { LocalStorage } from '@zos/storage'

const localStorage = new LocalStorage()

const gui = new AutoGUI();

// This variable will track our steps.
const stepCount = new Step();
const steps = stepCount.getCurrent();
let stepData = localStorage.getItem('state');

if (stepData) {
  let stepData = localStorage.getItem('state');
} else {
  stepData = steps;
}

let temp_steps_counter = stepData;

class IndexPage {
  init(){
    const fill_rects = gui.fillRect(0x6984CC);
    console.log("page is being built");
    let txt_steps = gui.text("Points: " + temp_steps_counter, { text_size: (480 - 368) / 2});

    gui.newRow();

    const bear_img = gui.image("bear.png");
    gui.newRow();

    const store_button = gui.button("Store", () => {
      // Take you to new store page
      temp_steps_counter = temp_steps_counter - 10
      localStorage.setItem('state', temp_steps_counter);
      push({
        url: "page/gt/food-list",
      });
   });
    AutoGUI.SetBtnRadius(30);

  
    // Render the GUI.
    gui.render();
    fill_rects.update({x:0, y:0, w:480, h:480});
    txt_steps.update({x: 0, w: 480, align_h: hmUI.align.CENTER_H});

    // Callbacks.
    const cb_Steps = () => {
      txt_steps.update({ text: "Points: " + temp_steps_counter });
      console.log(visits);
    };

    // and on change events
    steps.onChange(cb_Steps)
  }
}

Page({
  state: {
    data:null,
    storage: localStorage
  },
  build(){
    this.indexPage = new IndexPage();
    this.indexPage.init();
  },
  onDestroy(){
    this.indexPage.destroy();
    localStorage.setItem('state', this.state.data)
  }
})


/** HELPERS (Credit to Silver) */

// don't turn off the screen for 600 seconds
import { setPageBrightTime } from '@zos/display'
const result = setPageBrightTime({
  brightTime: 600 * 1000,
})

// don't turn off the screen on wrist down for 600 seconds
import { pauseDropWristScreenOff } from '@zos/display'
pauseDropWristScreenOff({
  duration: 600 * 1000,
})
```

### global.d.ts

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

```

### utils/storage.js

```javascript
import { statSync, writeFileSync, readFileSync } from "@zos/fs";

export default class LocalStorage {
  constructor(fileName = "") {
    this.fileName = fileName;
    this.contentObj = {};
  }

  set(obj) {
    writeFileSync({
      path: this.fileName,
      data: JSON.stringify(obj),
      options: {
        encoding: "utf8",
      },
    });
  }

  get() {
    const fStat = statSync({
      path: this.fileName,
    });
    if (fStat) {
      try {
        this.contentObj = JSON.parse(
          readFileSync({
            path: this.fileName,
            options: {
              encoding: "utf8",
            },
          })
        );
      } catch (error) {
        this.contentObj = {};
      }
    }

    return this.contentObj;
  }
}

```

### utils/constants.js

```javascript
import { getText } from "@zos/i18n";
import { getDeviceInfo, SCREEN_SHAPE_SQUARE } from "@zos/device";
import { Step } from "@zos/sensor";

const step = new Step();

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

export const total_steps = step.getCurrent();

export const FOOD_CALORIES = [
  {
    name: getText("hamburger"),
    type: "hamburger",
    value: 512,
  },
  {
    name: getText("chocolate"),
    type: "chocolate",
    value: 71,
  },
  {
    name: getText("cookie"),
    type: "cookies",
    value: 44,
  },
  {
    name: getText("cake"),
    type: "cake",
    value: 379,
  },
  {
    name: getText("pizza"),
    type: "pizza",
    value: 470,
  },
  {
    name: getText("sausage"),
    type: "sausage",
    value: 381,
  },
  {
    name: getText("ham"),
    type: "ham",
    value: 99,
  },
  {
    name: getText("iceCream"),
    type: "ice cream",
    value: 64,
  },
  {
    name: getText("coffee"),
    type: "coffee",
    value: 64,
  },
  {
    name: getText("beer"),
    type: "beer",
    value: 64,
  },
];
```

### page/gt/food-list.s.layout.js

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

export const COMMON_TITLE_TEXT = null;
export const FOOD_LIST_Y = px(96);
export const FOOD_LIST_ITEM_MARGIN = px(60);
export const FOOD_LIST_ITEM_HEIGHT = px(52);
export const FOOD_LIST_RADIOGROUP = {
  select_src: "selected.png",
  unselect_src: "unselected.png",
  x: px(30),
  y: FOOD_LIST_Y,
  w: -1,
  h: -1,
};

export const FOOD_LIST_RADIO_ITEM = {
  x: 0,
  y: FOOD_LIST_Y,
  w: FOOD_LIST_ITEM_HEIGHT,
  h: FOOD_LIST_ITEM_HEIGHT,
};

export const FOOD_LIST_RADIO_ITEM_TEXT = {
  x: px(106),
  y: px(97),
  w: px(254),
  h: FOOD_LIST_ITEM_HEIGHT,
  color: 0xffffff,
  text_size: px(32),
  align_h: hmUI.align.LEFT,
  align_v: hmUI.align.CENTER_V,
};

```

### page/gt/food-list.r.layout.js

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

import { DEVICE_WIDTH } from "../../utils/constants";

export const COMMON_TITLE_TEXT = {
  text: getText("Store"),
  x: px(96),
  y: px(40),
  w: DEVICE_WIDTH - 2 * px(96),
  h: px(46),
  color: 0xffffff,
  text_size: px(36),
  align_h: hmUI.align.CENTER_H,
  align_v: hmUI.align.CENTER_V,
  text_style: hmUI.text_style.WRAP,
};

export const FOOD_LIST_Y = px(178);
export const FOOD_LIST_ITEM_MARGIN = px(64);
export const FOOD_LIST_ITEM_HEIGHT = px(64);
export const FOOD_LIST_RADIOGROUP = {
  select_src: "selected.png",
  unselect_src: "unselected.png",
  x: px(44),
  y: px(FOOD_LIST_Y),
  w: -1,
  h: -1,
};

export const FOOD_LIST_RADIO_ITEM = {
  x: 0,
  y: 0,
  w: px(FOOD_LIST_ITEM_HEIGHT),
  h: px(FOOD_LIST_ITEM_HEIGHT),
};

export const FOOD_LIST_RADIO_ITEM_TEXT = {
  x: px(120),
  y: 0,
  w: px(360),
  h: px(64),
  color: 0xffffff,
  text_size: px(32),
  align_h: hmUI.align.LEFT,
  align_v: hmUI.align.CENTER_V,
};

```

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

```javascript
import * as hmUI from "@zos/ui";
import { getText } from "@zos/i18n";
import { px } from "@zos/utils";
import { DEVICE_WIDTH } from "../../utils/constants";

export const COMMON_TITLE_TEXT = null;
export const ALIGN_DESC_GROUP = {
  x: 0,
  y: px(80),
  w: 0,
  h: px(82),
};
export const IMGAE_CALORIES_MARIN = 0;
export const CALORIES_UNIT_MARIN = px(8);
export const CALORIE_TEXT_SIZE = px(72);
export const CALORIE_TEXT = {
  text: "400",
  x: 0,
  y: 0,
  w: 0,
  h: px(82),
  color: 0xffffff,
  text_size: CALORIE_TEXT_SIZE,
  align_h: hmUI.align.LEFT,
  align_v: hmUI.align.CENTER_V,
};
export const UNIT_TEXT_SIZE = px(24);
export const UNIT_TEXT = {
  text: getText("unit"),
  x: 0,
  y: px(46),
  w: 0,
  h: px(34),
  color: 0x999999,
  text_size: UNIT_TEXT_SIZE,
  align_h: hmUI.align.LEFT,
  align_v: hmUI.align.CENTER_V,
};

export const TOTAL_CONSUME_TEXT = {
  text: getText("consumption"),
  x: px(4),
  y: px(162),
  w: DEVICE_WIDTH - 2 * px(4),
  h: px(38),
  color: 0x999999,
  text_size: px(28),
  align_h: hmUI.align.CENTER_H,
  align_v: hmUI.align.CENTER_V,
};

export const EQUIVALENT_TO_BUTTON = {
  text: getText("equivalent"),
  press_color: 0x333333,
  normal_color: 0x1a1a1a,
  x: px(71),
  y: px(330),
  w: DEVICE_WIDTH - 2 * px(71),
  h: px(56),
  color: 0xffffff,
  text_size: px(28),
  radius: px(28),
};

export const CONSUME_ICON_WIDTH = px(32);
export const CONSUME_ICON = {
  src: "consume.png",
  x: 0,
  y: px(47),
};

export const EQUIVALENT_MARGIN = px(8);
export const EQUIVALENT_MORE_X = px(113);
export const EQUIVALENT_TO_FOOD_ICON_WIDTH = px(72);

export const EQUIVALENT_TO_FOOD_ICON = {
  src: "food/hamburger.png",
  x: px(103), //0
  y: px(224),
};

export const EQUIVALENT_MORE_FOOD_ICON = {
  src: "multiply.png",
  x: px(183),
  y: px(235),
};

export const EQUIVALENT_MORE_FOOD_NUM = {
  text: "",
  x: px(237),
  y: px(225),
  w: px(60),
  h: px(60),
  color: 0xee801e,
  text_size: px(50),
  align_h: hmUI.align.LEFT,
  align_v: hmUI.align.CENTER_V,
};

```

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

```javascript
import * as hmUI from "@zos/ui";
import { getText } from "@zos/i18n";
import { px } from "@zos/utils";
import { DEVICE_WIDTH } from "../../utils/constants";

export const COMMON_TITLE_TEXT = {
  text: getText("calories"),
  x: px(96),
  y: px(40),
  w: DEVICE_WIDTH - 2 * px(96),
  h: px(46),
  color: 0xffffff,
  text_size: px(36),
  align_h: hmUI.align.CENTER_H,
  align_v: hmUI.align.CENTER_V,
  text_style: hmUI.text_style.WRAP,
};

export const ALIGN_DESC_GROUP = {
  x: 0,
  y: px(118),
  w: 0,
  h: px(100),
};
export const IMGAE_CALORIES_MARIN = 0;
export const CALORIES_UNIT_MARIN = px(8);
export const CALORIE_TEXT_SIZE = px(100);
export const CALORIE_TEXT = {
  text: "",
  x: 0,
  y: 0,
  w: 0,
  h: px(100),
  color: 0xffffff,
  text_size: CALORIE_TEXT_SIZE,
  align_h: hmUI.align.LEFT,
  align_v: hmUI.align.CENTER_V,
};
export const UNIT_TEXT_SIZE = px(28);
export const UNIT_TEXT = {
  text: getText("unit"),
  x: 0,
  y: px(63),
  w: 0,
  h: px(34),
  color: 0x999999,
  text_size: UNIT_TEXT_SIZE,
  align_h: hmUI.align.LEFT,
  align_v: hmUI.align.CENTER_V,
};

export const TOTAL_CONSUME_TEXT = {
  text: getText("consumption"),
  x: px(40),
  y: px(218),
  w: DEVICE_WIDTH - 2 * px(40),
  h: px(38),
  color: 0x999999,
  text_size: px(28),
  align_h: hmUI.align.CENTER_H,
  align_v: hmUI.align.CENTER_V,
};

export const EQUIVALENT_TO_BUTTON = {
  text: getText("equivalent"),
  press_color: 0x333333,
  normal_color: 0x1a1a1a,
  x: px(108),
  y: px(376),
  w: DEVICE_WIDTH - 2 * px(108),
  h: px(56),
  color: 0xffffff,
  text_size: px(32),
  radius: px(28),
};

export const CONSUME_ICON_WIDTH = px(48);
export const CONSUME_ICON = {
  src: "consume.png",
  x: 0,
  y: px(53),
};

export const EQUIVALENT_MARGIN = px(8);
export const EQUIVALENT_MORE_X = px(155);
export const EQUIVALENT_TO_FOOD_ICON_WIDTH = px(80);

export const EQUIVALENT_TO_FOOD_ICON = {
  src: "food/hamburger.png",
  x: 0,
  y: px(288),
};

export const EQUIVALENT_MORE_FOOD_ICON = {
  src: "multiply.png",
  x: px(243),
  y: px(304),
};

export const EQUIVALENT_MORE_FOOD_NUM = {
  text: "",
  x: px(299),
  y: px(294),
  w: px(100),
  h: px(60),
  color: 0xee801e,
  text_size: px(55),
  align_h: hmUI.align.LEFT,
  align_v: hmUI.align.CENTER_V,
};

```

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