Build lightweight and fast battery monitor web app using SolidJS
Tags: solidjs, webdev, javascript
Published: 2022-03-30 10:20:00


Battery api provide an easy way to handle battery status in browser.

Battery api  can provide you following information:-

- Battery charging level  between `0.00 to 1.00` in decimal number. You can easily convert this decimal number into **percentage** by multiplying with `100`.

- Charging or Discharging boolean status
- Charging time in seconds
- Discharging time in seconds


## SolidJS
> Solid is a declarative JavaScript library for creating user interfaces. It does not use a Virtual DOM. Instead it opts to compile its templates down to real DOM nodes and wrap updates in fine grained reactions. This way when your state updates only the code that depends on it runs.
[SolidJS Documentation](https://www.solidjs.com/)


### Init new Solidjs project from Javascript template

``` 
 > npx degit solidjs/templates/js my-app
 > cd my-app
 > pnpm i
 > pnpm run dev

```


### Install Hope UI

```
pnpm add @hope-ui/solid @stitches/core solid-transition-group

```

### Open the project in vscode

```
code .

```


Lets start code by building a `useBattery` hook . This hook will call  web battery api and listen for battery event and update the store state

```
> mkdir hooks
> cd hooks
> touch useBattery.jsx

```

`useBattery.jsx`

```jsx
import { onMount } from "solid-js";
import { createStore } from "solid-js/store";
export default function useBattery() {
  const [store, setStore] = createStore({
    isSupported: true,
    charging: false,
    chargingTime: 0,
    dischargingTime: 0,
    level: 0,
  });
  onMount(async () => {
    try {
      const battery = await navigator.getBattery();

      console.log(battery);
      updateAllBatteryInfo(battery);

      battery.addEventListener("chargingchange", () => {
        updateChargeInfo(battery);
      });
      battery.addEventListener("levelchange", () => {
        updateLevelInfo(battery);
      });
      battery.addEventListener("chargingtimechange", () => {
        updateChargingInfo(battery);
      });
      battery.addEventListener("dischargingtimechange", () => {
        updateDischargingInfo(battery);
      });
    } catch (error) {
      console.log(error);
      setStore("isSupported", false);
    }
  });

  function updateAllBatteryInfo(battery) {
    updateChargeInfo(battery);
    updateLevelInfo(battery);
    updateChargingInfo(battery);
    updateDischargingInfo(battery);
  }

  function updateChargeInfo(battery) {
    setStore("charging", battery.charging);
  }

  function updateLevelInfo(battery) {
    setStore("level", battery.level);
  }

  function updateChargingInfo(battery) {
    setStore("chargingTime", battery.chargingTime);
  }

  function updateDischargingInfo(battery) {
    setStore("dischargingTime", battery.dischargingTime);
  }

  return {
    store,
  };
}


```


`onMount` is a lifecycle function in solidjs which run only once when component mount you can run side effect inside this function like api call. We use this function to add event listener for battery event and initialise store with initial value provided by battery api.

The `createStore` call takes the initial value (here we provide battery related fields) and returns a read/write tuple. The first element is the store proxy which is readonly, and the second is a setter function.


`navigator.getBattery()` call web battery api and return promise this resolves to `BatteryManager` Object contains information about charging level, charging time etc...

If  browser does not support battery web api then catch block will execute and it update `isSupported` to false and on ui screen we can show alert box.


`BatteryManager` Object  during discharging

```
{
  charging: false
  chargingTime: Infinity
  dischargingTime: 13684
  level: 0.62
}
```


`chargingchange` event fired when you connect or disconnect your device from charger.

`levelchange` event  fired when your device battery level change. Suppose your device is charging and their level change from 0.79 to 0.80 then this event will fired.

`chargingtimechange` event fired when battery charging time will updated

`dischargingtimechange` fired when battery discharging time will updated.


Lets build the ui to show the battery related data in `App.js`

```jsx

import {
  Alert,
  AlertDescription,
  AlertIcon,
  AlertTitle,
  Box,
  CircularProgress,
  CircularProgressIndicator,
  CircularProgressLabel,
  Container,
  GridItem,
  Heading,
  HopeProvider,
  HStack,
  SimpleGrid,
  Tag,
  Text,
  VStack,
} from "@hope-ui/solid";
import { Show } from "solid-js";
import ThemeSwitcher from "./components/ThemeSwitcher";
import useBattery from "./hooks/useBattery";

export default function App() {
  const { store } = useBattery();
  return (
    <HopeProvider config={{ initialColorMode: "dark" }}>
      <Box minH={"100vh"} h="$full" w={"$full"} py="$10">
        <VStack spacing={"$3"}>
          <Heading textAlign={"center"} fontSize={"$6xl"}>
            Battery &nbsp;
            <Box as="span" color={"$primary10"}>
              Monitor
            </Box>
          </Heading>

          <HStack spacing={"$2"}>
            <Show when={store.isSupported}>
              <Tag
                colorScheme={store.charging ? "success" : "danger"}
                size={"lg"}
                variant="dot"
                dotPlacement="start"
              >
                <Show when={store.charging} fallback="Discharging">
                  Charging
                </Show>
              </Tag>
            </Show>
            <ThemeSwitcher />
          </HStack>
        </VStack>
        <Container mt={"$10"}>
          <Show
            when={store.isSupported}
            fallback={
              <Alert
                status="danger"
                variant="subtle"
                flexDirection="column"
                justifyContent="center"
                textAlign="center"
                height="200px"
              >
                <AlertIcon boxSize="40px" mr="0" />
                <AlertTitle mt="$4" mb="$1" fontSize="$lg">
                  Unsupported Browser
                </AlertTitle>
                <AlertDescription maxWidth="$sm">
                  Your browser does not support Web Battery API
                </AlertDescription>
              </Alert>
            }
          >
            <SimpleGrid
              w={"$full"}
              columns={{ "@initial": 1, "@sm": 2, "@md": 3 }}
              justifyContent="center"
            >
              <GridItem mx={"auto"}>
                <CircularProgress thickness={"$0_5"} size={"$xs"} value={100}>
                  <CircularProgressIndicator color={"$warning10"} />
                  <CircularProgressLabel>
                    <VStack spacing={"$6"}>
                      <Heading fontSize={"$xl"}> Charging Time</Heading>
                      <Text fontSize={"$xl"}>
                        {Math.round(store.chargingTime / 60)} Minutes
                      </Text>
                    </VStack>
                  </CircularProgressLabel>
                </CircularProgress>
              </GridItem>
              <GridItem mx={"auto"}>
                <CircularProgress size={"$xs"} value={store.level * 100}>
                  <CircularProgressIndicator color={"$success10"} />
                  <CircularProgressLabel>
                    <VStack spacing={"$6"}>
                      <Heading fontSize={"$xl"}> Battery Level</Heading>
                      <Text fontSize={"$xl"}>{store.level * 100} %</Text>
                    </VStack>
                  </CircularProgressLabel>
                </CircularProgress>
              </GridItem>

              <GridItem mx={"auto"}>
                <CircularProgress thickness={"$0_5"} size={"$xs"} value={100}>
                  <CircularProgressIndicator color={"$primary10"} />
                  <CircularProgressLabel>
                    <VStack spacing={"$6"}>
                      <Heading fontSize={"$xl"}> Discharging Time</Heading>
                      <Text fontSize={"$xl"}>
                        {Math.round(store.dischargingTime / 60)} Minutes
                      </Text>
                    </VStack>
                  </CircularProgressLabel>
                </CircularProgress>
              </GridItem>
            </SimpleGrid>
          </Show>
        </Container>
      </Box>
    </HopeProvider>
  );
}


```


`ThemeSwitcher.jsx` component handle switching between dark and light mode.

```jsx


import { Button, useColorMode } from "@hope-ui/solid";

export default function ThemeSwitcher() {
  const { colorMode, toggleColorMode } = useColorMode();

  return (
    <Button
      rounded={"$full"}
      size="sm"
      colorScheme={"warning"}
      onClick={toggleColorMode}
    >
      {colorMode() === "light" ? "Light" : "Dark"} mode
    </Button>
  );
}


```



![Discharging State](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4l3llkh16v10e96o8r8.png)

![Charging Status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tvxmiknqr231vwbv84r9.png)


{% embed https://github.com/harshmangalam/solidjs-web-battery-monitor %}