Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

쨍쨍

#5. Al Smart Home_HomeActivty 본문

프로그래밍 코드/Android Studio

#5. Al Smart Home_HomeActivty

이선선 2024. 12. 17. 19:18

[ Home 화면 및 기능 ]

 

 

1 기상청  API를 사용하여 받아온 실외 온도와 습도, 미세먼지 값을 서버에서 받아와서 출력한다.
2 - 사용자 설정 Switch를 활성화하면 아두이노 온습도로 측정한 현재 실내온도와 비교하여 사용자가 원하는 실내온도를 설정할 수 있다. 설정 온도와 현재 온도를 비교하여 현재 온도보다 설정 온도가 더 높으면 히터가 작동되고, 현재 온도보다 설정 온도가 더 낮으면 에어컨이 작동한다.
- 추천온도 Switch를 활성화하면 실외온도, 현재 온도, 그리고 사용자가 설정한 온도를 데이터로 수집하여 딥러닝 모델이 학습한다. 이를 통해 사용자가 선호하는 특정 온도를 자동으로 설정할 수 있다.
3 Switch를 통해서 집 안의 조명, 에어컨, 히터, 공기청정기를 제어할 수 있다. 실제 가전제품과 연결하기에는 예산 상 제약으로 인한 어려움이 있었으나, 비슷한 기능을 제공하는 대체 센서(팬모터, LED 조명)을 활용하여 구현을 진행하였다.

 

 

 

[ activity_home.xml ]

 

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".HomeActivity">

    <ImageView
        android:id="@+id/voice"
        android:layout_width="320dp"
        android:layout_height="80dp"
        android:layout_gravity="center"
        android:layout_marginTop="5dp"
        android:background="@drawable/home1"/>

    <LinearLayout
        android:layout_width="350dp"
        android:layout_height="150dp"
        android:layout_gravity="center"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:background="@drawable/edge"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="50dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="100dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="5dp"
                android:gravity="center"
                android:text="실외온도"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <TextView
                android:id="@+id/out_temp"
                android:layout_width="95dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="150dp"
                android:gravity="center"
                android:text="  "
                android:textColor="@color/black"
                android:textSize="24sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="50dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginStart="5dp"
                android:gravity="center"
                android:text="실외습도"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <TextView
                android:id="@+id/out_humid"
                android:layout_width="95dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="150dp"
                android:gravity="center"
                android:text="  "
                android:textColor="@color/black"
                android:textSize="24sp" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="50dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="100dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="5dp"
                android:gravity="center"
                android:text="미세먼지"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <TextView
                android:id="@+id/microdust"
                android:layout_width="95dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="150dp"
                android:gravity="center"
                android:text="  "
                android:textColor="@color/black"
                android:textSize="24sp" />
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="350dp"
        android:layout_height="180dp"
        android:layout_gravity="center"
        android:layout_marginBottom="5dp"
        android:background="@drawable/edge"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="45dp"
            android:background="@drawable/edge"
            android:orientation="horizontal">

            <TextView
                android:layout_width="100dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="5dp"
                android:gravity="center"
                android:text="추천온도"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/TEMP_switch"
                android:layout_width="55dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="180dp" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="40dp"
            android:layout_marginTop="5dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="130dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:gravity="center"
                android:text="사용자 설정"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/set_switch"
                android:layout_width="55dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="155dp" />

        </LinearLayout>

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="40dp"
            android:orientation="horizontal"
            android:layout_marginTop="5dp">

            <TextView
                android:layout_width="100dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="80dp"
                android:gravity="center"
                android:text="현재 온도"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <TextView
                android:layout_width="100dp"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:layout_marginStart="20dp"
                android:gravity="center"
                android:text="설정 온도"
                android:textColor="@color/black"
                android:textSize="24sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="40dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="80dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:gravity="center"
                android:text="온도"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <TextView
                android:id="@+id/living_temp"
                android:layout_width="100dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:gravity="center"
                android:text="  "
                android:textColor="@color/black"
                android:textSize="24sp" />

            <EditText
                android:id="@+id/liv_con_temp"
                android:layout_width="100dp"
                android:layout_height="45dp"
                android:layout_gravity="center"
                android:layout_marginStart="20dp"
                android:gravity="center"
                android:hint="온도 입력"
                android:inputType="numberDecimal"
                android:maxLength="2"
                android:text=""
                android:textColor="@color/black"
                android:textSize="20sp" />

            <Button
                android:id="@+id/set_but"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:text="입력"
                />

        </LinearLayout>



    </LinearLayout>

    <LinearLayout
        android:layout_width="350dp"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:background="@drawable/edge"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="50dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="100dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:gravity="center"
                android:text="조명"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/LED_switch"
                android:layout_width="55dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="185dp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="50dp"
            android:orientation="horizontal">
            <TextView
                android:layout_width="100dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:gravity="center"
                android:text="에어컨"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/AC_switch"
                android:layout_width="55dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="185dp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="50dp"
            android:orientation="horizontal">

            <TextView
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:gravity="center"
                android:text="히터"
                android:textColor="@color/black"
                android:textSize="24sp" />

            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/HEATER_switch"
                android:layout_width="55dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="185dp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="350dp"
            android:layout_height="50dp"
            android:orientation="horizontal">
            <TextView
                android:layout_width="120dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:gravity="center"
                android:layout_marginStart="5dp"
                android:text="공기청정기"
                android:textColor="@color/black"
                android:textSize="24sp" />
            <androidx.appcompat.widget.SwitchCompat
                android:id="@+id/AIR_switch"
                android:layout_width="55dp"
                android:layout_height="40dp"
                android:layout_gravity="center"
                android:layout_marginStart="160dp" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

 

 

 

 

[ HomeActivity.java ]

 

 

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;

public class HomeActivity extends AppCompatActivity implements SocketActivity.OnDataReceivedListener {

    private static final String TAG = "HomeActivity";
    private TextView tempTextView, rehTextView, microTextView, livingtempView;
    private Handler uiHandler;
    private EditText livConTemp;
    private SwitchCompat ledSwitch,acSwitch, airSwitch, heaterSwitch, tempSwitch, setSwitch; // LED 스위치 추가
    private SocketActivity socketActivity;
    private Button setBut;

    private boolean isSetSwitchOn = false; // 스위치 상태를 저장하는 변수

    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);

        // UI 요소 초기화
        // findViewById로 레이아웃 XML의 UI 요소를 참조.
        tempTextView = findViewById(R.id.out_temp); // 실외 온도
        rehTextView = findViewById(R.id.out_humid); // 실외 습도
        microTextView = findViewById(R.id.microdust); // 실외 미세먼지
        livingtempView = findViewById(R.id.living_temp);
        livConTemp = findViewById(R.id.liv_con_temp);
        ledSwitch = findViewById(R.id.LED_switch); // LED 스위치 초기화
        acSwitch = findViewById(R.id.AC_switch); // AC 스위치 초기화
        airSwitch = findViewById(R.id.AIR_switch); // LED 스위치 초기화
        heaterSwitch = findViewById(R.id.HEATER_switch); // AC 스위치 초기화
        tempSwitch = findViewById(R.id.TEMP_switch); // 자동 온도 추천

        setSwitch = findViewById(R.id.set_switch);
        setBut = findViewById(R.id.set_but); // 설정온도입력 완료 버튼

        uiHandler = new Handler(Looper.getMainLooper());

        // SocketActivity 인스턴스를 가져와서 서버 연결 시작
        // SocketActivity 객체를 통해 서버 연결.
        socketActivity = SocketActivity.getInstance();
        socketActivity.connectToServer(this); // 서버 연결 및 데이터 수신

        // LED 스위치 리스너 설정
        // 스위치 상태에 따라 서버에 "LED_ON" 또는 "LED_OFF" 신호 전송.
        ledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                sendLedSignal("LED_ON");
            } else {
                sendLedSignal("LED_OFF");
            }
        });

        // 에어컨 스위치 리스너 설정
        acSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                sendLedSignal("AC_ON");
            } else {
                sendLedSignal("AC_OFF");
            }
        });

        // 공기청정기 스위치 리스너 설정
        airSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                sendLedSignal("PFAN_ON");
            } else {
                sendLedSignal("PFAN_OFF");
            }
        });

        // 히터 스위치 리스너 설정
        heaterSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                sendLedSignal("HFAN_ON");
            } else {
                sendLedSignal("HFAN_OFF");
            }
        });

        // 온도 추천 스위치 리스너 설정
        tempSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                sendLedSignal("Recommend_ON");
            } else {
                sendLedSignal("Recommend_OFF");
            }
        });

        // set_switch 스위치 리스너 설정
        setSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
            isSetSwitchOn = isChecked; // 스위치 상태 저장

            if (!isChecked) {
                // 스위치가 왼쪽으로 갔을 때 No_Signal, HFAN_OFF, AC_OFF 전송
                sendLedSignal("No_Signal");
                sendLedSignal("HFAN_OFF");
                sendLedSignal("AC_OFF");
            }
        });

        // set_but 버튼 클릭 리스너 설정
        setBut.setOnClickListener(v -> {
            if (isSetSwitchOn) {
                // 스위치가 오른쪽으로 켜져 있을 때만 compareAndSendSignal 호출
                compareAndSendSignal();
            }
        });
    }

    // 서버에 LED 신호를 전송하는 메서드
    private void sendLedSignal(String signal) {
        if (socketActivity != null) {
            socketActivity.sendData(signal);
            Log.d(TAG, "Sent LED signal: " + signal); // 신호 전송 시 로그 작성
        } else {
            Log.e(TAG, "SocketActivity is null, cannot send signal.");
        }
    }


    // 서버에서 데이터 수신 시 호출되는 메서드
    // parseAndSetData(data)로 데이터를 분석 및 UI 업데이트, 데이터가 "BTN_ON"이면 팝업창 표시.
    @Override
    public void onDataReceived(String data) {
        Log.d(TAG, "Received: " + data); // 수신 데이터 로그 출력
        parseAndSetData(data);

        if (data.equals("BTN_ON")) {
            runOnUiThread(this::showAlertForBtnOn); // UI 스레드에서 팝업 실행
        }

    }

    // 비상버튼 팝업창
    private void showAlertForBtnOn() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        // 커스텀 레이아웃 인플레이션
        LayoutInflater inflater = getLayoutInflater();
        View dialogView = inflater.inflate(R.layout.emergency, null);

        // AlertDialog에 커스텀 레이아웃 설정
        builder.setView(dialogView)
                .setTitle("알림")
                .setPositiveButton("확인", (dialog, which) -> dialog.dismiss())
                .show();
    }

    // 데이터를 파싱하고 UI에 설정하는 메서드
    private void parseAndSetData(String data) {
        Log.d(TAG, "Parsing Data: " + data); // 전체 데이터 확인

        if (data.startsWith("OUTTEMP:") && data.length() > "OUTTEMP:".length()) { // 실외 온도 데이터
            String OUTTEMP = data.substring("OUTTEMP:".length()).trim();
            tempTextView.setText(OUTTEMP + " °C"); // 온도 설정
        } else if (data.startsWith("REH:") && data.length() > "REH:".length()) { // 실외 습도 데이터
            String reh = data.substring("REH:".length()).trim();
            rehTextView.setText(reh + " %"); // 습도 설정
        } else if (data.startsWith("MICRO:") && data.length() > "MICRO:".length()) { // 미세먼지 데이터
            String micro = data.substring("MICRO:".length()).trim();
            try {
                int microValue = Integer.parseInt(micro);
                updateMicroStatus(microValue); // 미세먼지 상태 설정
            } catch (NumberFormatException e) {
                Log.e(TAG, "Invalid micro data: " + micro);
                microTextView.setText("데이터 없음");
            }

        } else if (data.startsWith("INTEMP:") && data.length() > "INTEMP:".length()) { // 실내 온도 데이터
            String INTEMP = data.substring("INTEMP:".length()).trim();
            livingtempView.setText(INTEMP + " °C"); // 실내 온도 설정

        }
    }

    // 온도 비교 후 신호 전송하는 메서드
    private void compareAndSendSignal() {
        try {
            String livingTempText = livingtempView.getText().toString().replace(" °C", "").trim();
            String targetTempText = livConTemp.getText().toString().trim();

            if (!livingTempText.isEmpty() && !targetTempText.isEmpty()) {
                float livingTemp = Float.parseFloat(livingTempText);
                float targetTemp = Float.parseFloat(targetTempText);

                if (livingTemp > targetTemp) {
                    sendLedSignal("AC_ON");
                    sendLedSignal("HFAN_OFF");
                    sendLedSignal("usersettemp, " + targetTemp);
                } else if (livingTemp < targetTemp) {
                    sendLedSignal("HFAN_ON");
                    sendLedSignal("AC_OFF");
                    sendLedSignal("usersettemp, " + targetTemp);
                } else {
                    sendLedSignal("No_Signal");
                }
            }
        } catch (NumberFormatException e) {
            Log.e(TAG, "Invalid temperature input", e);
        }
    }

    // 미세먼지 상태를 업데이트하는 메서드
    private void updateMicroStatus(int microValue) {
        String status;
        if (microValue <= 30) {
            status = "좋음"; // 미세먼지 상태가 좋음
        } else if (microValue <= 80) {
            status = "보통"; // 미세먼지 상태가 보통
        } else if (microValue <= 150) {
            status = "나쁨"; // 미세먼지 상태가 나쁨
        } else {
            status = "매우 나쁨"; // 미세먼지 상태가 매우 나쁨
        }
        microTextView.setText(status); // 텍스트 뷰에 설정
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 액티비티 종료 시 서버 연결 해제
        if (socketActivity != null) {
            socketActivity.disconnect();
        }
    }
}