How To Create a Single Page ReactJs Dashboard for Tinxy Smart Switches

Ashok Raja T
Technology Specialist
February 7, 2022
Rate this article
Views    8267

In this article, let us see how to create a ReactJs Web Application that can act as a dashboard for Tinxy Smart Switches. Tinxy provides a mobile application to manage their devices. But, I wished to have a Web Application as I would be in front of the laptop for most of my time.

Dashboard for Tinxy Smart Switches

This application allows you to only view the list of Tinxy Smart Switches and control it from a browser. Adding or managing Tinxy Smart Switches has to be done with their Mobile App.

An API Key created from the Tinxy mobile app is required to run this application. Follow the steps in this article to create an API Key.

I have created this as a self-contained file with ReactJs and Ant UI Framework. To run this application, create a file named index.html and copy & paste the HTML content shown below. Before you run the application, modify line 39 with your API Key.

Application Source code

Below is the source code of the entire application.

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Tinxy Control Panel</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1, width=device-width" />
  <script src="https://unpkg.com/react@latest/umd/react.development.js" crossorigin="anonymous"></script>
  <script src="https://unpkg.com/react-dom@latest/umd/react-dom.development.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/antd/4.18.5/antd.min.js"></script>
  <script src="https://unpkg.com/babel-standalone@latest/babel.min.js" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/ant-design-icons/4.7.0/index.umd.min.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/4.18.5/antd.min.css" />
  <style>
    .ant-list-item-meta-title {
      font-size: 18px;
      margin-bottom: -6px !important;
    }
    .ant-list-item-meta-description {
      font-size: 15px;
    }
    h3 {
      font-weight: normal;
      text-align: center;
      padding-top: 20px;
    }
    .ant-list-item-meta-avatar {
      height: 45px;
      padding-top: 5px;
      padding-left: 10px;
    }
  </style>
</head>

<body>
  <div id="root"></div>
  <script type="text/babel">

    const TINXY_API_KEY = 'REPLACE_THIS_WITH_YOUR_API_KEY';
    const TINXY_BASE_URL = 'https://backend.tinxy.in/v2/';
    const ICONS_BASE_URL = 'https://img.icons8.com/';

    const { Row, Col, Divider, List, Avatar, Switch, Typography } = antd;
    const { CloseOutlined, CheckOutlined, WifiOutlined } = icons;

    function get_Logo(str) {
      switch (true) {
        case /.*fan.*/i.test(str):
          return ICONS_BASE_URL + "ios/50/000000/fan-speed--v2.png";
        case /.*tube.*/i.test(str):
        case /.*light.*/i.test(str):
        case /.*bulb.*/i.test(str):
          return ICONS_BASE_URL + "wired/64/000000/light-automation.png";
        default:
          return ICONS_BASE_URL + "external-parzival-1997-detailed-outline-parzival-1997/64/000000/external-home-control-technology-in-daily-life-parzival-1997-detailed-outline-parzival-1997.png";
      }
    }

    function App() {
      const [error, setError] = React.useState(null);
      const [items, setItems] = React.useState([]);

      React.useEffect(() => {
        fetch(TINXY_BASE_URL + "devices/", {
          headers: { 'Authorization': 'Bearer ' + TINXY_API_KEY }
        }).then(res => res.json()).then(async (dv) => {
          dv.map(async (item) => {
            item.devices.map(async (dt, i) => {
              fetch(`${TINXY_BASE_URL}devices/${item._id}/state?deviceNumber=${i + 1}`, {
                headers: { 'Authorization': 'Bearer ' + TINXY_API_KEY }
              }).then(res => res.json()).then((ds) => {

                setItems(prevState => [...prevState, {
                  'device_group': item.name,
                  'device_g_id': item._id,
                  'device_index': i + 1,
                  'device_name': item.devices[i],
                  'device_type': item.deviceTypes[i],
                  'device_state': ds.state == "ON" ? true : false,
                  'device_image': get_Logo(item.deviceTypes[i]),
                  'item_loading': false
                }]);
              })

            })
          })
        },
          (error) => {
            setError(error);
          }
        )
      }, []);

      function toggleState(item, itemIndex, isLoading, deviceState) {
        const data = [...items];
        data[itemIndex].item_loading = isLoading
        data[itemIndex].device_state = deviceState
        setItems(data);
      }

      return (
        <div>
          <Row justify="center">
            <Col span={6} justify="center">
              <Typography.Title level={3} type="primary">Tinxy Control Panel</Typography.Title>
            </Col>
          </Row>

          <Row justify="center">
            <Col span={6}>
              <List
                itemLayout="horizontal"
                dataSource={items}
                renderItem={item => (
                  <List.Item>
                    <List.Item.Meta avatar={<Avatar src={item.device_image} />}
                      title={item.device_name} description={item.device_group} />
                    <Switch
                      checkedChildren={<CheckOutlined />} unCheckedChildren={<CloseOutlined />}
                      checked={item.device_state} loading={item.item_loading}
                      onClick={() => {

                        const itemIndex = items.findIndex((dt) =>
                          dt.device_g_id == item.device_g_id &&
                          dt.device_index == item.device_index);

                        toggleState(item, itemIndex, true, !item.device_state)

                        fetch(`${TINXY_BASE_URL}devices/${item.device_g_id}/toggle`, {
                          method: "post",
                          crossDomain: true,
                          headers: { "Content-Type": "application/json", 'Authorization': 'Bearer ' + TINXY_API_KEY },
                          body: JSON.stringify({ request: { state: item.device_state ? 1 : 0 }, deviceNumber: item.device_index })
                        }).then(res => res.json()).then((dt) => {
                          toggleState(item, itemIndex, false, item.device_state)
                        }).catch((e) => {
                          toggleState(item, itemIndex, false, !item.device_state)
                          setError(e);
                        });
                      }} />
                  </List.Item>
                )}
              />
            </Col>
          </Row>
        </div>
      );
    }

    ReactDOM.render(
      <App />,
      document.querySelector('#root'),
    );
  </script>
</body>
</html>

This would work even if you directly open the file in a browser, But I would suggest having a mini webserver similar to http-server to run this application. If all goes well, you would be able to see a screen similar to the one shown below.

Tinxy Web Dashboard

I am running this application in my Synology NAS so that it is accessible across my Home Network.

Subscribe To Our Newsletter
Loading

Leave a comment