Skip to content

A smart home system where you can make any IoT smart device with the help of an ESP32, NextJS, Docker, Kubernetes and MSSQL

License

Notifications You must be signed in to change notification settings

martinvichnal/smart-home

Repository files navigation

Smart home system


Introduction

NOTE: There are major bugs, inefficiency and security issues. This is not at all the best implementation of the system. I just flew too close to the sun and wanted to see what I am capable of.

This is the repository of my custom built smart home system. This system is a dynamically changeable smart home system built with React TypeScript, firabase and ESP32 devices.

  • Feel free to improve, use or fork this repository in your own projects :)
  • For any bugs or improvements feel free to make an issue or make a pull request

Table of Contents


Features

  • Dynamically add custom variables to the system
  • Fast and reliable communication
  • Many virtual homes within one ESP
  • Easy implementation

Hardware Requirements

  • Some computer to run the docker containers (preferably raspberry pi or host it on your preferable server hoster)
  • Any ESP32
  • Sensors or devices of your choise

Software Dependencies

  • Docker (running the webapp and the servers)
  • SmartHome library (or the following librarys if you don't want to use the dedicated library)
  • On the Webapp side you can find the dependencies in the package.json file

Installation and usage

  1. Clone the repository
  2. Configure and install the components

Create an IoT device

  • Create a virtual smart home with the SmartHome class with your server urls and device properties:
SmartHome desk("Desk", "1", "1", "http://0.0.0.0:0000");
  • Add custom bool or int to your SmartHome:
desk.addVariableBool(13, "deskLamp", deskLamp);
  • Validate the home:
    • Note: before validation the ESP32 needs to connect to your local network
desk.validateHome();
  • Initialize the WebSocket connection:
ws.begin(webSocketServerAddress, webSocketServerPort, "/socket.io/?EIO=4");
ws.onEvent(webSocketEvents);
  • Call the WebSocket loop in the loop section:
ws.loop();
  • Stringify the home then send it at your neede
String dataDesk = desk.prepareWebSocketData();
ws.sendEVENT(dataDesk);

WebApp

npm run dev

Servers

For running the servers you need to build then execute it on the docker app

WebSocket

docker build . -t smart-home-websocket
docker run -p 5000:5000 smart-home-websocket

API

docker build . -t smart-home-api
docker run -p 8080:8080 smart-home-api

MSSQL

Pulling the Microsoft SQL database docker images from Microsoft

docker pull mcr.microsoft.com/mssql/server:2022-latest

Creating docker container from the image with cusomt name --name iot-database and password "MSSQL_SA_PASSWORD=ThisIsThePassword!24" (A strong system administrator (SA) password: At least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.)

docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=ThisIsThePassword!24" -p 1433:1433 -d --name iot-database mcr.microsoft.com/mssql/server:2022-latest

Demo data injection

-- Create the devices table
CREATE TABLE devices (
    did VARCHAR(255),
    dn VARCHAR(255),
    dd VARCHAR(MAX),
    uid VARCHAR(255)
);

-- Insert data into the devices table
INSERT INTO devices (did, dn, dd, uid)
VALUES 
    ('1001', 'Device1', 'property1-n-0-100-50--property2-b-0-1-0--', '1124'),
    ('1002', 'Device2', 'setting1-b-0-1-1--setting2-n-0-10-5--', '1124'),
    ('1003', 'Device3', 'temperature-n-0-50-25--humidity-n-0-100-75--', '1124'),
    ('1004', 'Device4', 'state-b-0-1-1--brightness-n-0-255-128--', '1124');

Configuration

In order to use the system you have to configure the webapp and the ESP32 to connect to the servers via IP and their port and also you have to set your wifi ssid and password credentials in your ESP32 project


How it works ?


System


User Interface


IoT Device


Servers

Websocket

At the top, the necessary modules are imported. Express is a web application framework for Node.js, designed for building web applications and APIs. Socket.IO is a JavaScript library for real-time web applications. It enables real-time, bidirectional, and event-based communication between the browser and the server.

The connectedClients Map is used to keep track of all connected clients. Each client is identified by a unique clientId which is a combination of the user's userId, deviceId, clientType, and the socket's id.

The sendConnectionStatusEvent function is used to send a connectionStatus event to a specific socket. It sends an object containing an array of connectedDevices for a specific userId.

The io.on("connection", (socket) => {...}) block is where the server handles a new client connection. Inside this block, several event listeners are set up on the socket object to handle different types of events.

The join event is used when a client wants to join the server. The client sends its userId, deviceId, and clientType to the server. The server then stores this information in the socket object and the connectedClients Map.

If the clientType is webapp, the server sets up a webMessage event listener on the socket. When a webMessage event is received, the server tries to find the target device's socket and sends a message event to it.

If the clientType is device, the server sets up a deviceMessage event listener on the socket. When a deviceMessage event is received, the server sends a message event to all webapp sockets associated with the userId.

The disconnect event is used when a client disconnects from the server. The server removes the client's information from the connectedClients Map.

Finally, the server starts listening on a specific port for incoming connections.

WebSocket API Documentation

Connection

To connect to the WebSocket server, create a new WebSocket instance in your client-side JavaScript:

const socket = io('<http://localhost:5000>');

Replace '<http://localhost:5000>' with the URL of your WebSocket server.

Joining

After connecting, you should emit a "join" event to the server. This event should include your user ID, device ID, and client type:

socket.emit('join', userId, deviceId, clientType);
  • userId (string): The ID of the user.
  • deviceId (string): The ID of the device. This is not required for webapp clients.
  • clientType (string): The type of the client. This should be either "webapp" or "device".

Sending Messages

To send a message, emit a ${clientType}Message event to the server. This event should include the target device ID and the message:

socket.emit(`${clientType}Message`, targetDeviceId, message);
  • targetDeviceId (string): The ID of the target device. This is required for webapp clients.
  • message (string): The message to send.

Receiving Messages

To receive messages, listen for the "message" event:

socket.on('message', (message) => {
  console.log('Received message:', message);
});

Connection Status

To receive updates about the connection status, listen for the "connectionStatus" event:

socket.on('connectionStatus', (data) => {
  console.log('Connected devices:', data.connectedDevices);
});

Disconnecting

To disconnect from the server, use the disconnect method:

socket.disconnect();

Please note that this is a basic API documentation. Depending on your application's requirements, you might need to add more events or data fields.

REST API

Database

/**
 * @description Get a device from the database by ID or by user ID
 * @method GET
 * @API /api/devices
 * @API /api/devices/device?did={did}
 * @API /api/device/device?uid={uid}
 * @body -
 * @returns {JSON} result
 */
export async function GET(request)

/**
 * @description Update device data in the database by ID
 * @method PUT
 * @API /api/devices/device
 * @body {JSON} {did, dd}
 * @returns {JSON} result
 */
export async function PUT(request)

/**
 * @description Add (insert) a device to the database
 * @method POST
 * @API /api/devices
 * @body {JSON} {did, dn, dd, uid}
 * @returns {JSON} result
 */
export async function POST(request)

/**
 * @description Delete a device from the database by ID
 * @method DELETE
 * @API /api/devices/device
 * @body {JSON} {did}
 * @returns {JSON} result
 */
export async function DELETE(request)

USERS:

/**
 * @description Get all user from the database or by ID
 * @method GET
 * @API /api/users
 * @API /api/users/user?uid={uid}
 * @body -
 * @returns {JSON} result
 */
export async function GET(request)

/**
 * @description Update user data in the database by ID
 * @method PUT
 * @API /api/users/user
 * @body {JSON} {uid}
 * @returns {JSON} result
 */
export async function PUT(request)

/**
 * @description Add (insert) a user to the database
 * @method POST
 * @API /api/users
 * @body {JSON} {uid}
 * @returns {JSON} result
 */
export async function POST(request)

/**
 * @description Delete a user from the database by ID
 * @method DELETE
 * @API /api/users/user
 * @body {JSON} {uid}
 * @returns {JSON} result
 */
export async function DELETE(request)

Example:

https://myserver.com/api/devices/device?did=05b31779-14ee-4233-8c9a-2749e81d3ccb -> GET request
-> response:
        {
            "DID": "05b31779-14ee-4233-8c9a-2749e81d3ccb",
            "DN": "Thermostat",
            "DD": "temperature-n-0-100-34--humidity-n-0-100-61--state-b-0-0-0--",
            "UID": "80ff2b60-bf4b-42fe-8de4-d21734a393c8"
        },

https://myserver.com/api/devices/device -> PUT request
-> request body:
        {
            "DID": "05b31779-14ee-4233-8c9a-2749e81d3ccb",
            "DN": "Thermostat",
            "DD": "temperature-n-0-100-34--humidity-n-0-100-61--state-b-0-0-0--",
            "UID": "80ff2b60-bf4b-42fe-8de4-d21734a393c8"
        },
-> response: "Device updated successfully"
[
  {
    "did": "1111",
    "dn": "Led",
    "dd": "ledVariable-b-0-0-true--",
    "uid": "1124"
  },
  {
    "did": "123",
    "dn": "Desk",
    "dd": "deskLamp-b-0-0-true--deskLampBrightness-n-0-255-20--deskMonitor-b-0-0-true--",
    "uid": "1124"
  },
  {
    "did": "456",
    "dn": "Thermostat",
    "dd": "thermostatTemperature-n-0-100-13--thermostatHumidity-n-0-100-31--thermostatPower-b-0-0-true--",
    "uid": "1124"
  },
  {
    "did": "789",
    "dn": "Bed",
    "dd": "bedLamp-b-0-0-false--bedLampBrightness-n-0-255-225--",
    "uid": "1124"
  }
]
[
  {
    "UID": "09c007bd-526b-4d8e-a9b9-96daff857759"
  },
  {
    "UID": "d480b324-d6bd-4e05-820f-c807a7a5ed7e"
  }
]
export const user = {
    uid: "1124",
    uName: "Vichnál Martin",
    uPhoto: "https://lh3.googleusercontent.com/a/ACg8ocI5cTr4KR7TWUMmnwHdRFaBpEZw6QRUiwtVixPCTQVmuow=s96-c",
    uIsAuth: true,
}

Acknowledgements / Source