icon

UNIHIKER Smart Home Terminal

A Centralized Smart-Home System for Controlling and Monitoring IoT Devices

Introduction

In the rapidly evolving world of Internet of Things (IoT), there's a growing need for centralized, user-friendly control systems that can manage multiple smart devices efficiently. This project presents a smart home terminal using the UNIHIKER platform, addressing key challenges in current IoT ecosystems and providing a versatile solution for home automation.

 

 

Project Overview

The UNIHIKER Smart Home Terminal serves as a central control system for IoT devices, allowing users to monitor sensor data from various rooms and control appliances throughout their home. By leveraging the UNIHIKER's capabilities and integrating it with ESP32 modules, this project creates a robust, scalable smart home solution.

 

 

 

 

Challenges in Current IoT Platforms

Several challenges in existing IoT platforms motivated the development of this project:

 

1. Complexity: Setting up and managing multiple IoT devices can be complex for average users, often requiring separate apps or interfaces for each device

2. Fragmentation: Many IoT devices use proprietary protocols, leading to a fragmented ecosystem where devices from different manufacturers don't communicate effectively.

3. Privacy Concerns: Cloud-dependent IoT solutions raise privacy concerns as sensitive data is transmitted and stored on external servers.

4. Reliability: Internet-dependent IoT systems may fail during network outages, rendering smart home features unusable.

5. Scalability: Many current solutions struggle to scale efficiently as users add more devices to their smart home setup. 

 

UNIHIKER Solution

The UNIHIKER Smart Home Terminal addresses these challenges by:

1. Centralization: Providing a single interface to control and monitor all connected devices, regardless of manufacturer.

2. Simplicity: Offering an intuitive touchscreen interface that's easy for users to understand and operate.

3. Local Processing: Performing data processing and storage locally on the UNIHIKER and its SD card, enhancing privacy and reducing cloud dependence.

4. Offline Functionality: Maintaining core functionalities even when internet connectivity is lost.

5. Expandability: Easily integrating new devices and sensors through the UNIHIKER's versatile interfaces. 

 

 

Why UNIHIKER?

1. Built-in touchscreen display for instant visual interface.

2. Python compatibility for rapid development and access to extensive libraries.

3. Versatile connectivity options (Wi-Fi, Bluetooth, GPIO) for easy integration with various smart home devices.

4. Wireless programming

5. Compact, energy-efficient design suitable for 24/7 operation as a home control hub.

6. Secure local processing enhancing privacy and reducing reliance on cloud services.

 

Find out more about the UNIHIKER on their website: https://www.unihiker.com/

 

System Architecture

 

The system consists of three main components

UNIHIKER Central Terminal: Acts as the hub, processing data and providing the user interface.

ESP32 Nodes: Deployed in various rooms to collect sensor data and control appliances.

Local Network: Facilitates communication between the UNIHIKER and ESP32 nodes.

 

 

 

Features

 

Real-time Monitoring: Display current temperature and humidity for each room.

Appliance Control: Toggle lights, fans, ACs, and other appliances in different rooms.

Intuitive Interface: Easy-to-use touchscreen interface with icon-based navigation.

Expandability: Easily add new rooms or appliances to the system.

Offline Operation: Core functionalities work without internet connectivity.

 

Hardware Components

UNIHIKER board

Multiple ESP32 modules

Various sensors (temperature, humidity)Relay modules for appliance control

LEDs 

 

Software Implementation

The software is implemented in Python, utilizing the following key components:

 

Tkinter GUI: Creates an intuitive interface for users to interact with the system.

Socket Programming: Enables communication between the UNIHIKER and ESP32 nodes.

SON Data Format: Used for structured data exchange between devices.

Threading: Allows for concurrent operations, such as periodic data updates and user interactions.

PIL (Python Imaging Library): Handles image processing for icons and visual elements. 

 

 

Key features of the implementation include:

Real-time clock display

Room-specific dashboards showing temperature and humidity

Appliance control toggles/Buttons

Navigation between different system pages (Dashboard, Alerts, Automation, Settings)

 

Step-by-step Guide

 

1. Setting Up the UNIHIKER

 

 

 

UNIHIKER2.png

 

 

 

 

 

1. Setting up the Unihiker

Power on your Unihiker by connecting it to a power source using the USB-C port.Connect to the Unihiker's Wi-Fi hotspot. The SSID should be "Unihiker-XXXX" where XXXX are the last four characters of the device's MAC address.Open a web browser and navigate to http://192.168.4.1. This will open the Unihiker web interface.Follow the on-screen instructions to connect your Unihiker to your local Wi-Fi network.Once connected, the Unihiker will display its IP address on the screen. Note this down for future use.

 

2. Accessing Jupyter Notebook

On a device connected to the same network as your Unihiker, open a web browser.Enter the IP address of your Unihiker followed by ":8888". For example: http://192.168.1.100:8888You should now see the Jupyter Notebook interface.

 

3. Installing Required Libraries

In the Jupyter Notebook interface, click on "New" and select "Terminal".In the terminal, use pip to install the required libraries:Note: tkinter should already be installed in the Unihiker's Python environment.

 

4. Creating and Running the Smart Home Dashboard

In the Jupyter Notebook interface, click on "New" and select "Python 3".In the new notebook, copy and paste the entire Smart Home Dashboard code we developed earlier.Save the notebook with a name like "SmartHomeDashboard.ipynb".

 

 

You can download the code in this repository 

 

 

 

5. Preparing Icon Files

Prepare the following icon files:

temperature.pnghumidity.pngliving_room.pngbedroom.pngkitchen.pngbathroom.pngdashboard.pngalerts.pngautomation.pngsettings.png 

 

Upload these files to the same directory as your Jupyter Notebook:In the Jupyter interface, click the "Upload" button.Select all the icon files and upload them.

 

6. Running the Dashboard

In your Jupyter Notebook with the Smart Home Dashboard code, run all cells by selecting "Cell" > "Run All" from the menu.The dashboard should now start running on your Unihiker's display. 

7. You cal altenatively run the program by accessing the unihihiker menu then click option 

4-Run Programs, then click on /root then now run your notebook or code file

 

 

 

 

 

2. Setting Up Each ESP32

 

Install Arduino IDE

Download and install the Arduino IDE on your computer.Add ESP32 board support to Arduino IDE. 

 

Connect Sensors and Relays

Wire the DHT sensor to the designated pin (e.g., D1).Connect relays for appliance control to the specified pins. 

 

Download the code from the Project's Repository

 

 

 

Upload the Arduino Code

Open the provided ESP32 code in Arduino IDE.Update Wi-Fi credentials:Modify pin assignments if necessary:Select your ESP32 board from Tools > Board menu.Choose the correct port from Tools > Port.Click the Upload button to flash the code to your ESP32. 

 

Verify Connection

Open the Serial Monitor (Tools > Serial Monitor).Set the baud rate to 115200.You should see the ESP32's IP address printed once it connects to Wi-Fi. 

 

Repeat for Each Room

Follow steps 2-4 for each ESP32 in your system.

Note down the IP address of each ESP32 for use in the UNIHIKER code. 

 

Final Steps

 

Ensure Network Connectivity

Make sure the UNIHIKER and all ESP32s are on the same Wi-Fi network. 

 

Update UNIHIKER Code

If needed, update the ESP32 IP addresses in the UNIHIKER Python script. 

 

Test the System

Run the UNIHIKER script.Verify that you can see all rooms and control the appliances. 

With these steps completed, your UNIHIKER Smart Home Terminal should be operational, communicating with the ESP32 nodes in each room.

 

 

 

Key components of the GUI:

 

 

 

 

 

Room and Dashboard Classes

 

 

 

How Everything Works

 

The UNIHIKER Smart Home Terminal project creates a centralized, customizable home automation system using a UNIHIKER board as the main hub and multiple ESP32 microcontrollers as room-specific nodes. 

The UNIHIKER, equipped with a touchscreen display, runs a Python-based graphical user interface that allows users to monitor and control various aspects of their home environment. Each room is outfitted with an ESP32 that connects to sensors (such as temperature and humidity) and controls appliances (like lights, fans, or heating systems). 

The UNIHIKER communicates with these ESP32 nodes over a local WiFi network using a simple yet effective protocol based on JSON-formatted messages. 

Users can view real-time data from each room, toggle appliances on or off, and potentially set up automated routines. 

The system's modular design allows for easy expansion, enabling homeowners to add new rooms or devices as needed. 

This DIY approach to smart home technology offers a flexible, cost-effective alternative to commercial solutions, empowering users to tailor their home automation setup to their specific needs and preferences.

 

 

Expanding the System

 

To add a new room or device:

 

1. Configure a new ESP32 with the provided code.2. Update the UNIHIKER dashboard to include the new room/device.

 

-You can create a new room, assign its appliances and add the ip address of the ESP32 connceted-You can also add or remove the appliances in the existing rooms

 

 

 

Results

After following these steps, you'll have a fully functional home control system. The UNIHIKER's screen will display your home's vital statistics and allow you to control connected devices with a simple touch.

 

 

DEMO TIME!!

 

Watch the video at the end to see the project in action.

 

 

Future Improvements

While the current system provides a solid foundation for home automation, there's always room for improvement:

1. Energy Monitoring: Integrate power consumption tracking for appliances.

2. Voice Control: Add voice recognition capabilities for hands-free operation.

3. Machine Learning: Implement predictive algorithms for automated climate control.

4. Mobile App: Develop a companion mobile app for remote access.

5. Advanced Automation: Create complex routines and scenarios for home automation

6. Voice Control: Add voice recognition capabilities for hands-free operation.

 

Conclusion

 

The UNIHIKER Home Control System demonstrates the power of open-source hardware and software in creating practical, customizable smart home solutions. By following this guide, you've not only built a powerful home automation system but also gained valuable skills in embedded systems programming, networking, and UI design.

 

Collaboration/ Contributing

I encourage you to experiment with the system, add your own features, and share your improvements with the community.

 

Link to GitHub Repository

 

Remember to document your journey, including any challenges you faced and how you overcame them. This not only helps others learn from your experience but also makes your project more relatable and engaging

 

If you're interested in collaborating to improve or customize this project, or if you'd like to explore potential partnerships and promotions, feel free to reach out. Let's work together to make it even more versatile and impactful! Looking forward to hearing from you.

Happy hacking!

 

CODE
import tkinter as tk
import socket
import json
import threading
import time
from pillow import Image, ImageTk

class Room:
    def __init__(self, name, controls, ip_address, icon_file):
        self.name = name
        self.temperature = 25
        self.humidity = 57
        self.appliances = {control: False for control in controls}
        self.ip_address = ip_address
        self.icon_file = icon_file

class Dashboard:
    def __init__(self, master):
        self.master = master
        master.title("SMART HOME")
        master.configure(bg="#2c3e50")
        
        self.rooms = [
            Room("Living Room", ["Light", "TV", "AC", "Fan"], "192.168.137.236", "living-room.png"),
            Room("Bedroom", ["Light", "Fan", "Heater"], "192.168.137.59", "bedroom.png"),
            Room("Kitchen", ["Light", "Oven", "Fridge"], "192.168.1.103", "kitchen.png"),
            Room("Bathroom", ["Light"], "192.168.1.104", "bathroom.png")
        ]
        self.current_room = None
        
        # Load and resize icons
        self.temp_icon = self.load_and_resize_icon("temp.png", (35, 35))
        self.humid_icon = self.load_and_resize_icon("humidity.png", (35, 35))
        
        # Start a thread for periodic updates
        self.update_thread = threading.Thread(target=self.periodic_update, daemon=True)
        self.update_thread.start()

        self.create_home_page()

    def load_and_resize_icon(self, filename, size):
        try:
            icon = Image.open(filename)
            icon = icon.resize(size, Image.ANTIALIAS)
            return ImageTk.PhotoImage(icon)
        except FileNotFoundError:
            print(f"Warning: Icon file {filename} not found.")
            return None

    def create_home_page(self):
        for widget in self.master.winfo_children():
            widget.destroy()
        
        tk.Label(self.master, text="SMART HOME", font=("Arial", 18, "bold"), bg="#2c3e50", fg="#ecf0f1").pack(pady=5)
        
        # Digital clock display
        self.clock_frame = tk.Frame(self.master, bg="#34495e", bd=2, relief=tk.RAISED)
        self.clock_frame.pack(fill=tk.X, padx=10, pady=3)
        self.time_label = tk.Label(self.clock_frame, font=("DS-Digital", 20), bg="#34495e", fg="#2ecc71")
        self.time_label.pack(pady=3)
        self.date_label = tk.Label(self.clock_frame, font=("Arial", 10), bg="#34495e", fg="#ecf0f1")
        self.date_label.pack(pady=3)
        self.update_clock()

        # Navigation icons
        icon_frame = tk.Frame(self.master, bg="#2c3e50")
        icon_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=3)
        icon_frame.grid_columnconfigure((0,1), weight=1)
        icon_frame.grid_rowconfigure((0,1), weight=1)

        self.create_icon(icon_frame, "Dashboard", "dashboard.png", self.create_main_dashboard, 0, 0)
        self.create_icon(icon_frame, "Alerts", "alerts.png", self.show_alerts, 0, 1)
        self.create_icon(icon_frame, "Automation", "automation.png", self.show_automation, 1, 0)
        self.create_icon(icon_frame, "Settings", "settings.png", self.show_settings, 1, 1)

    def create_icon(self, parent, text, icon_file, command, row, col):
        icon_frame = tk.Frame(parent, bg="#34495e", bd=0, relief=tk.RAISED)
        icon_frame.grid(row=row, column=col, sticky="nsew", padx=5, pady=5)

        try:
            icon = Image.open(icon_file)
            icon = icon.resize((50, 50), Image.ANTIALIAS)
            icon_photo = ImageTk.PhotoImage(icon)
            icon_button = tk.Button(icon_frame, image=icon_photo, bg="#34495e", bd=0, command=command)
            icon_button.image = icon_photo  # Keep a reference
            icon_button.pack(pady=(10,5))
        except FileNotFoundError:
            # Fallback to text button if icon file is not found
            icon_button = tk.Button(icon_frame, text=text, font=("Arial", 14), bg="#3498db", fg="#ffffff",
                                    command=command, width=10, height=2)
            icon_button.pack(pady=(10,5))

        tk.Label(icon_frame, text=text, font=("Arial", 12), bg="#34495e", fg="#ecf0f1").pack()

    def update_clock(self):
        current_time = time.strftime("%H:%M:%S")
        current_date = time.strftime("%B %d, %Y")
        self.time_label.config(text=current_time)
        self.date_label.config(text=current_date)
        self.master.after(1000, self.update_clock)

    def create_main_dashboard(self):
        for widget in self.master.winfo_children():
            widget.destroy()
        tk.Label(self.master, text="SMART HOME", font=("Arial", 20, "bold"), bg="#2c3e50", fg="#ecf0f1").pack(pady=5)
        
        icon_frame = tk.Frame(self.master, bg="#2c3e50")
        icon_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        icon_frame.grid_columnconfigure((0,1), weight=1)
        icon_frame.grid_rowconfigure((0,1), weight=1)

        for i, room in enumerate(self.rooms):
            self.create_icon(icon_frame, room.name, room.icon_file, lambda r=room: self.show_room_dashboard(r), i//2, i%2)

        self.add_back_button(self.create_home_page)

    def show_room_dashboard(self, room):
        self.current_room = room
        for widget in self.master.winfo_children():
            widget.destroy()
        tk.Label(self.master, text=room.name, font=("Arial", 18, "bold"), bg="#2c3e50", fg="#ecf0f1").pack(pady=10)
        
        # Sensor display
        sensor_frame = tk.Frame(self.master, bg="#34495e", bd=0, relief=tk.RAISED)
        sensor_frame.pack(fill=tk.X, padx=10, pady=5)
        
        # Temperature display
        temp_frame = tk.Frame(sensor_frame, bg="#34495e")
        temp_frame.pack(side=tk.LEFT, expand=True, pady=5, padx=5)
        if self.temp_icon:
            tk.Label(temp_frame, image=self.temp_icon, bg="#34495e").pack(side=tk.LEFT, padx=(0, 5))
        self.temp_label = tk.Label(temp_frame, text=f"{room.temperature}°C", font=("Arial", 15), bg="#34495e", fg="#ffffff")
        self.temp_label.pack(side=tk.LEFT)
        
        # Humidity display
        humid_frame = tk.Frame(sensor_frame, bg="#34495e")
        humid_frame.pack(side=tk.RIGHT, expand=True, pady=5, padx=5)
        if self.humid_icon:
            tk.Label(humid_frame, image=self.humid_icon, bg="#34495e").pack(side=tk.LEFT, padx=(0, 5))
        self.humid_label = tk.Label(humid_frame, text=f"{room.humidity}%", font=("Arial", 15), bg="#34495e", fg="#ffffff")
        self.humid_label.pack(side=tk.LEFT)
        
        # Controls
        control_frame = tk.Frame(self.master, bg="#2c3e50")
        control_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        for i, (appliance, status) in enumerate(room.appliances.items()):
            button = tk.Button(
                control_frame,
                text=f"{appliance}: {'On' if status else 'Off'}",
                command=lambda a=appliance: self.toggle_appliance(a),
                font=("Arial", 12),
                bg="#2ecc71" if status else "#e74c3c",
                fg="#ffffff",
                width=15, height=2,
                relief=tk.FLAT,
            )
            button.grid(row=i//2, column=i%2, pady=5, padx=5, sticky="nsew")
        control_frame.grid_columnconfigure(0, weight=1)
        control_frame.grid_columnconfigure(1, weight=1)
        
        self.add_back_button(self.create_main_dashboard)

    def add_back_button(self, command):
        tk.Button(
            self.master, 
            text="Back", 
            command=command, 
            font=("Arial", 12), 
            bg="#7f8c8d", 
            fg="#ffffff",
            width=20, height=2,
            relief=tk.FLAT,
        ).pack(pady=5)

    def toggle_appliance(self, appliance):
        if self.current_room:
            new_status = not self.current_room.appliances[appliance]
            self.current_room.appliances[appliance] = new_status
            self.send_command(self.current_room.ip_address, appliance, new_status)
            self.show_room_dashboard(self.current_room)  # Refresh the room dashboard

    def send_command(self, ip_address, appliance, status):
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((ip_address, 8080))
                command = json.dumps({"command": "set", "appliance": appliance, "status": status})
                s.sendall(command.encode())
                response = s.recv(1024).decode()
                print(f"Response from {ip_address}: {response}")
        except Exception as e:
            print(f"Error sending command to {ip_address}: {e}")

    def get_room_data(self, ip_address):
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((ip_address, 8080))
                command = json.dumps({"command": "get"})
                s.sendall(command.encode())
                response = s.recv(1024).decode()
                return json.loads(response)
        except Exception as e:
            print(f"Error getting data from {ip_address}: {e}")
            return None

    def update_room_data(self, room, data):
        if data:
            room.temperature = data.get('temperature', room.temperature)
            room.humidity = data.get('humidity', room.humidity)
            room.appliances.update(data.get('appliances', {}))

    def periodic_update(self):
        while True:
            for room in self.rooms:
                data = self.get_room_data(room.ip_address)
                self.update_room_data(room, data)
            
            if self.current_room:
                self.master.after(0, self.update_current_room_display)
            
            time.sleep(5)  # Update every 5 seconds

    def update_current_room_display(self):
        if self.current_room:
            self.temp_label.config(text=f"{self.current_room.temperature}°C")
            self.humid_label.config(text=f"{self.current_room.humidity}%")

    # Placeholder methods for new pages
    def show_alerts(self):
        self.show_placeholder_page("Alerts")

    def show_automation(self):
        self.show_placeholder_page("Automation")

    def show_settings(self):
        self.show_placeholder_page("Settings")

    def show_placeholder_page(self, title):
        for widget in self.master.winfo_children():
            widget.destroy()
        tk.Label(self.master, text=title, font=("Arial", 20, "bold"), bg="#2c3e50", fg="#ecf0f1").pack(pady=20)
        tk.Label(self.master, text=f"This is the {title} page", font=("Arial", 14), bg="#2c3e50", fg="#ecf0f1").pack(pady=20)
        self.add_back_button(self.create_home_page)

def main():
    root = tk.Tk()
    root.geometry("240x320")
    dashboard = Dashboard(root)
    root.mainloop()

if __name__ == "__main__":
    main()
CODE
import tkinter as tk
import socket
import json
import threading
import time
from pillow import Image, ImageTk

class Room:
    def __init__(self, name, controls, ip_address, icon_file):
        self.name = name
        self.temperature = 25
        self.humidity = 57
        self.appliances = {control: False for control in controls}
        self.ip_address = ip_address
        self.icon_file = icon_file

class Dashboard:
    def __init__(self, master):
        self.master = master
        master.title("SMART HOME")
        master.configure(bg="#2c3e50")
        
        self.rooms = [
            Room("Living Room", ["Light", "TV", "AC", "Fan"], "192.168.137.236", "living-room.png"),
            Room("Bedroom", ["Light", "Fan", "Heater"], "192.168.137.59", "bedroom.png"),
            Room("Kitchen", ["Light", "Oven", "Fridge"], "192.168.1.103", "kitchen.png"),
            Room("Bathroom", ["Light"], "192.168.1.104", "bathroom.png")
        ]
        self.current_room = None
        
        # Load and resize icons
        self.temp_icon = self.load_and_resize_icon("temp.png", (35, 35))
        self.humid_icon = self.load_and_resize_icon("humidity.png", (35, 35))
        
        # Start a thread for periodic updates
        self.update_thread = threading.Thread(target=self.periodic_update, daemon=True)
        self.update_thread.start()

        self.create_home_page()

    def load_and_resize_icon(self, filename, size):
        try:
            icon = Image.open(filename)
            icon = icon.resize(size, Image.ANTIALIAS)
            return ImageTk.PhotoImage(icon)
        except FileNotFoundError:
            print(f"Warning: Icon file {filename} not found.")
            return None

    def create_home_page(self):
        for widget in self.master.winfo_children():
            widget.destroy()
        
        tk.Label(self.master, text="SMART HOME", font=("Arial", 18, "bold"), bg="#2c3e50", fg="#ecf0f1").pack(pady=5)
        
        # Digital clock display
        self.clock_frame = tk.Frame(self.master, bg="#34495e", bd=2, relief=tk.RAISED)
        self.clock_frame.pack(fill=tk.X, padx=10, pady=3)
        self.time_label = tk.Label(self.clock_frame, font=("DS-Digital", 20), bg="#34495e", fg="#2ecc71")
        self.time_label.pack(pady=3)
        self.date_label = tk.Label(self.clock_frame, font=("Arial", 10), bg="#34495e", fg="#ecf0f1")
        self.date_label.pack(pady=3)
        self.update_clock()

        # Navigation icons
        icon_frame = tk.Frame(self.master, bg="#2c3e50")
        icon_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=3)
        icon_frame.grid_columnconfigure((0,1), weight=1)
        icon_frame.grid_rowconfigure((0,1), weight=1)

        self.create_icon(icon_frame, "Dashboard", "dashboard.png", self.create_main_dashboard, 0, 0)
        self.create_icon(icon_frame, "Alerts", "alerts.png", self.show_alerts, 0, 1)
        self.create_icon(icon_frame, "Automation", "automation.png", self.show_automation, 1, 0)
        self.create_icon(icon_frame, "Settings", "settings.png", self.show_settings, 1, 1)

    def create_icon(self, parent, text, icon_file, command, row, col):
        icon_frame = tk.Frame(parent, bg="#34495e", bd=0, relief=tk.RAISED)
        icon_frame.grid(row=row, column=col, sticky="nsew", padx=5, pady=5)

        try:
            icon = Image.open(icon_file)
            icon = icon.resize((50, 50), Image.ANTIALIAS)
            icon_photo = ImageTk.PhotoImage(icon)
            icon_button = tk.Button(icon_frame, image=icon_photo, bg="#34495e", bd=0, command=command)
            icon_button.image = icon_photo  # Keep a reference
            icon_button.pack(pady=(10,5))
        except FileNotFoundError:
            # Fallback to text button if icon file is not found
            icon_button = tk.Button(icon_frame, text=text, font=("Arial", 14), bg="#3498db", fg="#ffffff",
                                    command=command, width=10, height=2)
            icon_button.pack(pady=(10,5))

        tk.Label(icon_frame, text=text, font=("Arial", 12), bg="#34495e", fg="#ecf0f1").pack()

    def update_clock(self):
        current_time = time.strftime("%H:%M:%S")
        current_date = time.strftime("%B %d, %Y")
        self.time_label.config(text=current_time)
        self.date_label.config(text=current_date)
        self.master.after(1000, self.update_clock)

    def create_main_dashboard(self):
        for widget in self.master.winfo_children():
            widget.destroy()
        tk.Label(self.master, text="SMART HOME", font=("Arial", 20, "bold"), bg="#2c3e50", fg="#ecf0f1").pack(pady=5)
        
        icon_frame = tk.Frame(self.master, bg="#2c3e50")
        icon_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        icon_frame.grid_columnconfigure((0,1), weight=1)
        icon_frame.grid_rowconfigure((0,1), weight=1)

        for i, room in enumerate(self.rooms):
            self.create_icon(icon_frame, room.name, room.icon_file, lambda r=room: self.show_room_dashboard(r), i//2, i%2)

        self.add_back_button(self.create_home_page)

    def show_room_dashboard(self, room):
        self.current_room = room
        for widget in self.master.winfo_children():
            widget.destroy()
        tk.Label(self.master, text=room.name, font=("Arial", 18, "bold"), bg="#2c3e50", fg="#ecf0f1").pack(pady=10)
        
        # Sensor display
        sensor_frame = tk.Frame(self.master, bg="#34495e", bd=0, relief=tk.RAISED)
        sensor_frame.pack(fill=tk.X, padx=10, pady=5)
        
        # Temperature display
        temp_frame = tk.Frame(sensor_frame, bg="#34495e")
        temp_frame.pack(side=tk.LEFT, expand=True, pady=5, padx=5)
        if self.temp_icon:
            tk.Label(temp_frame, image=self.temp_icon, bg="#34495e").pack(side=tk.LEFT, padx=(0, 5))
        self.temp_label = tk.Label(temp_frame, text=f"{room.temperature}°C", font=("Arial", 15), bg="#34495e", fg="#ffffff")
        self.temp_label.pack(side=tk.LEFT)
        
        # Humidity display
        humid_frame = tk.Frame(sensor_frame, bg="#34495e")
        humid_frame.pack(side=tk.RIGHT, expand=True, pady=5, padx=5)
        if self.humid_icon:
            tk.Label(humid_frame, image=self.humid_icon, bg="#34495e").pack(side=tk.LEFT, padx=(0, 5))
        self.humid_label = tk.Label(humid_frame, text=f"{room.humidity}%", font=("Arial", 15), bg="#34495e", fg="#ffffff")
        self.humid_label.pack(side=tk.LEFT)
        
        # Controls
        control_frame = tk.Frame(self.master, bg="#2c3e50")
        control_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        for i, (appliance, status) in enumerate(room.appliances.items()):
            button = tk.Button(
                control_frame,
                text=f"{appliance}: {'On' if status else 'Off'}",
                command=lambda a=appliance: self.toggle_appliance(a),
                font=("Arial", 12),
                bg="#2ecc71" if status else "#e74c3c",
                fg="#ffffff",
                width=15, height=2,
                relief=tk.FLAT,
            )
            button.grid(row=i//2, column=i%2, pady=5, padx=5, sticky="nsew")
        control_frame.grid_columnconfigure(0, weight=1)
        control_frame.grid_columnconfigure(1, weight=1)
        
        self.add_back_button(self.create_main_dashboard)

    def add_back_button(self, command):
        tk.Button(
            self.master, 
            text="Back", 
            command=command, 
            font=("Arial", 12), 
            bg="#7f8c8d", 
            fg="#ffffff",
            width=20, height=2,
            relief=tk.FLAT,
        ).pack(pady=5)

    def toggle_appliance(self, appliance):
        if self.current_room:
            new_status = not self.current_room.appliances[appliance]
            self.current_room.appliances[appliance] = new_status
            self.send_command(self.current_room.ip_address, appliance, new_status)
            self.show_room_dashboard(self.current_room)  # Refresh the room dashboard

    def send_command(self, ip_address, appliance, status):
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((ip_address, 8080))
                command = json.dumps({"command": "set", "appliance": appliance, "status": status})
                s.sendall(command.encode())
                response = s.recv(1024).decode()
                print(f"Response from {ip_address}: {response}")
        except Exception as e:
            print(f"Error sending command to {ip_address}: {e}")

    def get_room_data(self, ip_address):
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((ip_address, 8080))
                command = json.dumps({"command": "get"})
                s.sendall(command.encode())
                response = s.recv(1024).decode()
                return json.loads(response)
        except Exception as e:
            print(f"Error getting data from {ip_address}: {e}")
            return None

    def update_room_data(self, room, data):
        if data:
            room.temperature = data.get('temperature', room.temperature)
            room.humidity = data.get('humidity', room.humidity)
            room.appliances.update(data.get('appliances', {}))

    def periodic_update(self):
        while True:
            for room in self.rooms:
                data = self.get_room_data(room.ip_address)
                self.update_room_data(room, data)
            
            if self.current_room:
                self.master.after(0, self.update_current_room_display)
            
            time.sleep(5)  # Update every 5 seconds

    def update_current_room_display(self):
        if self.current_room:
            self.temp_label.config(text=f"{self.current_room.temperature}°C")
            self.humid_label.config(text=f"{self.current_room.humidity}%")

    # Placeholder methods for new pages
    def show_alerts(self):
        self.show_placeholder_page("Alerts")

    def show_automation(self):
        self.show_placeholder_page("Automation")

    def show_settings(self):
        self.show_placeholder_page("Settings")

    def show_placeholder_page(self, title):
        for widget in self.master.winfo_children():
            widget.destroy()
        tk.Label(self.master, text=title, font=("Arial", 20, "bold"), bg="#2c3e50", fg="#ecf0f1").pack(pady=20)
        tk.Label(self.master, text=f"This is the {title} page", font=("Arial", 14), bg="#2c3e50", fg="#ecf0f1").pack(pady=20)
        self.add_back_button(self.create_home_page)

def main():
    root = tk.Tk()
    root.geometry("240x320")
    dashboard = Dashboard(root)
    root.mainloop()

if __name__ == "__main__":
    main()
License
All Rights
Reserved
licensBg
0