manhteky123 commited on
Commit
0213045
·
verified ·
1 Parent(s): c5c5946

Upload 7 files

Browse files
Files changed (7) hide show
  1. Dockerfile +19 -0
  2. README.md +22 -12
  3. app.py +56 -0
  4. requirements.txt +2 -0
  5. static/css/style.css +201 -0
  6. static/js/script.js +148 -0
  7. templates/index.html +35 -0
Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Copy requirements first for better caching
7
+ COPY requirements.txt .
8
+
9
+ # Install dependencies
10
+ RUN pip install --no-cache-dir -r requirements.txt
11
+
12
+ # Copy application files
13
+ COPY . .
14
+
15
+ # Expose port 7860 (Hugging Face Spaces default port)
16
+ EXPOSE 7860
17
+
18
+ # Run the application
19
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,12 +1,22 @@
1
- ---
2
- title: Light
3
- emoji: 💻
4
- colorFrom: green
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 6.0.2
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Light Control
3
+ emoji: 💡
4
+ colorFrom: gray
5
+ colorTo: yellow
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ # Hệ Thống Điều Khiển Đèn Thông Minh
11
+
12
+ Ứng dụng web đơn giản để điều khiển đèn với giao diện tối giản.
13
+
14
+ ## Tính năng
15
+ - 💡 Bật/tắt đèn qua giao diện web
16
+ - 🎨 Giao diện tối giản, dễ sử dụng
17
+ - 🔄 Tự động cập nhật trạng thái
18
+
19
+ ## Công nghệ
20
+ - Flask (Python)
21
+ - HTML/CSS/JavaScript
22
+ - Docker
app.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, jsonify, request
2
+ from flask_cors import CORS
3
+
4
+ app = Flask(__name__)
5
+ CORS(app)
6
+
7
+ # Trạng thái đèn (lưu trong memory, có thể thay bằng database)
8
+ light_status = {
9
+ 'on': False
10
+ }
11
+
12
+ @app.route('/')
13
+ def index():
14
+ """Trang chủ với giao diện điều khiển đèn"""
15
+ return render_template('index.html')
16
+
17
+ @app.route('/api/light/status', methods=['GET'])
18
+ def get_light_status():
19
+ """API lấy trạng thái đèn hiện tại"""
20
+ return jsonify({
21
+ 'status': 'on' if light_status['on'] else 'off',
22
+ 'on': light_status['on']
23
+ })
24
+
25
+ @app.route('/api/light/toggle', methods=['POST'])
26
+ def toggle_light():
27
+ """API bật/tắt đèn"""
28
+ light_status['on'] = not light_status['on']
29
+ return jsonify({
30
+ 'status': 'on' if light_status['on'] else 'off',
31
+ 'on': light_status['on'],
32
+ 'message': 'Đèn đã bật' if light_status['on'] else 'Đèn đã tắt'
33
+ })
34
+
35
+ @app.route('/api/light/on', methods=['POST'])
36
+ def turn_on():
37
+ """API bật đèn"""
38
+ light_status['on'] = True
39
+ return jsonify({
40
+ 'status': 'on',
41
+ 'on': True,
42
+ 'message': 'Đèn đã bật'
43
+ })
44
+
45
+ @app.route('/api/light/off', methods=['POST'])
46
+ def turn_off():
47
+ """API tắt đèn"""
48
+ light_status['on'] = False
49
+ return jsonify({
50
+ 'status': 'off',
51
+ 'on': False,
52
+ 'message': 'Đèn đã tắt'
53
+ })
54
+
55
+ if __name__ == '__main__':
56
+ app.run(debug=False, host='0.0.0.0', port=7860)
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ Flask==3.0.0
2
+ flask-cors==4.0.0
static/css/style.css ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
9
+ background: #f5f5f5;
10
+ min-height: 100vh;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ padding: 20px;
15
+ }
16
+
17
+ .container {
18
+ background: white;
19
+ border-radius: 10px;
20
+ padding: 40px;
21
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
22
+ max-width: 400px;
23
+ width: 100%;
24
+ text-align: center;
25
+ }
26
+
27
+ h1 {
28
+ color: #333;
29
+ margin-bottom: 30px;
30
+ font-size: 1.8em;
31
+ font-weight: 600;
32
+ }
33
+
34
+ .light-container {
35
+ margin: 40px 0;
36
+ }
37
+
38
+ .bulb-wrapper {
39
+ position: relative;
40
+ display: inline-block;
41
+ }
42
+
43
+ .bulb {
44
+ font-size: 100px;
45
+ transition: all 0.2s ease;
46
+ filter: grayscale(100%);
47
+ position: relative;
48
+ display: inline-block;
49
+ }
50
+
51
+ .bulb.on {
52
+ filter: grayscale(0%);
53
+ }
54
+
55
+ .bulb-glow {
56
+ display: none;
57
+ }
58
+
59
+ .status-text {
60
+ font-size: 1.2em;
61
+ font-weight: 500;
62
+ margin-top: 20px;
63
+ color: #666;
64
+ transition: color 0.2s ease;
65
+ }
66
+
67
+ .status-text.on {
68
+ color: #333;
69
+ }
70
+
71
+ .controls {
72
+ margin: 30px 0;
73
+ }
74
+
75
+ .btn {
76
+ padding: 12px 24px;
77
+ font-size: 1em;
78
+ border: 1px solid #ddd;
79
+ border-radius: 5px;
80
+ cursor: pointer;
81
+ transition: all 0.2s ease;
82
+ font-weight: 500;
83
+ margin: 8px;
84
+ background: white;
85
+ color: #333;
86
+ }
87
+
88
+ .btn:hover {
89
+ background: #f8f8f8;
90
+ border-color: #bbb;
91
+ }
92
+
93
+ .btn:active {
94
+ background: #e8e8e8;
95
+ }
96
+
97
+ .btn-toggle {
98
+ background: #333;
99
+ color: white;
100
+ border-color: #333;
101
+ width: 100%;
102
+ font-size: 1em;
103
+ }
104
+
105
+ .btn-toggle:hover {
106
+ background: #555;
107
+ border-color: #555;
108
+ }
109
+
110
+ .button-group {
111
+ display: flex;
112
+ justify-content: center;
113
+ gap: 10px;
114
+ margin-top: 20px;
115
+ }
116
+
117
+ .btn-on {
118
+ background: white;
119
+ color: #333;
120
+ flex: 1;
121
+ }
122
+
123
+ .btn-on:hover {
124
+ background: #f8f8f8;
125
+ }
126
+
127
+ .btn-off {
128
+ background: white;
129
+ color: #333;
130
+ flex: 1;
131
+ }
132
+
133
+ .btn-off:hover {
134
+ background: #f8f8f8;
135
+ }
136
+
137
+ .info-panel {
138
+ background: #fafafa;
139
+ border-radius: 5px;
140
+ padding: 15px;
141
+ margin-top: 25px;
142
+ border: 1px solid #e0e0e0;
143
+ }
144
+
145
+ .info-panel h3 {
146
+ color: #555;
147
+ margin-bottom: 10px;
148
+ font-size: 1em;
149
+ font-weight: 600;
150
+ }
151
+
152
+ .info-item {
153
+ display: flex;
154
+ justify-content: space-between;
155
+ align-items: center;
156
+ padding: 8px 0;
157
+ }
158
+
159
+ .info-item span:first-child {
160
+ color: #666;
161
+ font-weight: 400;
162
+ }
163
+
164
+ .status-badge {
165
+ padding: 4px 12px;
166
+ border-radius: 3px;
167
+ font-weight: 500;
168
+ font-size: 0.85em;
169
+ }
170
+
171
+ .status-badge.on {
172
+ background: #333;
173
+ color: white;
174
+ }
175
+
176
+ .status-badge.off {
177
+ background: #e0e0e0;
178
+ color: #666;
179
+ }
180
+
181
+ @media (max-width: 480px) {
182
+ .container {
183
+ padding: 20px;
184
+ }
185
+
186
+ h1 {
187
+ font-size: 1.5em;
188
+ }
189
+
190
+ .bulb {
191
+ font-size: 80px;
192
+ }
193
+
194
+ .button-group {
195
+ flex-direction: column;
196
+ }
197
+
198
+ .btn-on, .btn-off {
199
+ width: 100%;
200
+ }
201
+ }
static/js/script.js ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Cập nhật giao diện dựa trên trạng thái đèn
2
+ function updateUI(isOn) {
3
+ const bulb = document.getElementById('bulb');
4
+ const statusText = document.getElementById('statusText');
5
+ const currentStatus = document.getElementById('currentStatus');
6
+
7
+ if (isOn) {
8
+ bulb.classList.add('on');
9
+ statusText.classList.add('on');
10
+ statusText.textContent = 'Đèn đang bật';
11
+ currentStatus.textContent = 'BẬT';
12
+ currentStatus.classList.remove('off');
13
+ currentStatus.classList.add('on');
14
+ } else {
15
+ bulb.classList.remove('on');
16
+ statusText.classList.remove('on');
17
+ statusText.textContent = 'Đèn đang tắt';
18
+ currentStatus.textContent = 'TẮT';
19
+ currentStatus.classList.remove('on');
20
+ currentStatus.classList.add('off');
21
+ }
22
+ }
23
+
24
+ // Lấy trạng thái đèn hiện tại
25
+ async function getStatus() {
26
+ try {
27
+ const response = await fetch('/api/light/status');
28
+ const data = await response.json();
29
+ updateUI(data.on);
30
+ } catch (error) {
31
+ console.error('Lỗi khi lấy trạng thái:', error);
32
+ alert('Không thể kết nối với server!');
33
+ }
34
+ }
35
+
36
+ // Bật/tắt đèn
37
+ async function toggleLight() {
38
+ try {
39
+ const response = await fetch('/api/light/toggle', {
40
+ method: 'POST',
41
+ headers: {
42
+ 'Content-Type': 'application/json'
43
+ }
44
+ });
45
+ const data = await response.json();
46
+ updateUI(data.on);
47
+ showNotification(data.message);
48
+ } catch (error) {
49
+ console.error('Lỗi khi chuyển đổi đèn:', error);
50
+ alert('Không thể kết nối với server!');
51
+ }
52
+ }
53
+
54
+ // Bật đèn
55
+ async function turnOn() {
56
+ try {
57
+ const response = await fetch('/api/light/on', {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Content-Type': 'application/json'
61
+ }
62
+ });
63
+ const data = await response.json();
64
+ updateUI(data.on);
65
+ showNotification(data.message);
66
+ } catch (error) {
67
+ console.error('Lỗi khi bật đèn:', error);
68
+ alert('Không thể kết nối với server!');
69
+ }
70
+ }
71
+
72
+ // Tắt đèn
73
+ async function turnOff() {
74
+ try {
75
+ const response = await fetch('/api/light/off', {
76
+ method: 'POST',
77
+ headers: {
78
+ 'Content-Type': 'application/json'
79
+ }
80
+ });
81
+ const data = await response.json();
82
+ updateUI(data.on);
83
+ showNotification(data.message);
84
+ } catch (error) {
85
+ console.error('Lỗi khi tắt đèn:', error);
86
+ alert('Không thể kết nối với server!');
87
+ }
88
+ }
89
+
90
+ // Hiển thị thông báo
91
+ function showNotification(message) {
92
+ const notification = document.createElement('div');
93
+ notification.className = 'notification';
94
+ notification.textContent = message;
95
+ notification.style.cssText = `
96
+ position: fixed;
97
+ top: 20px;
98
+ right: 20px;
99
+ background: #333;
100
+ color: white;
101
+ padding: 12px 20px;
102
+ border-radius: 5px;
103
+ box-shadow: 0 2px 8px rgba(0,0,0,0.15);
104
+ z-index: 1000;
105
+ font-size: 0.9em;
106
+ animation: slideIn 0.3s ease;
107
+ `;
108
+
109
+ document.body.appendChild(notification);
110
+
111
+ setTimeout(() => {
112
+ notification.style.animation = 'slideOut 0.3s ease';
113
+ setTimeout(() => notification.remove(), 300);
114
+ }, 2500);
115
+ }
116
+
117
+ // Thêm CSS cho animation
118
+ const style = document.createElement('style');
119
+ style.textContent = `
120
+ @keyframes slideIn {
121
+ from {
122
+ transform: translateX(400px);
123
+ opacity: 0;
124
+ }
125
+ to {
126
+ transform: translateX(0);
127
+ opacity: 1;
128
+ }
129
+ }
130
+
131
+ @keyframes slideOut {
132
+ from {
133
+ transform: translateX(0);
134
+ opacity: 1;
135
+ }
136
+ to {
137
+ transform: translateX(400px);
138
+ opacity: 0;
139
+ }
140
+ }
141
+ `;
142
+ document.head.appendChild(style);
143
+
144
+ // Lấy trạng thái khi trang load
145
+ window.addEventListener('DOMContentLoaded', getStatus);
146
+
147
+ // Cập nhật trạng thái mỗi 5 giây
148
+ setInterval(getStatus, 5000);
templates/index.html ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="vi">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Điều Khiển Đèn Thông Minh</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <h1>🏠 Điều Khiển Đèn Thông Minh</h1>
12
+
13
+ <div class="light-container">
14
+ <div class="bulb-wrapper">
15
+ <div class="bulb" id="bulb">
16
+ <div class="bulb-glow"></div>
17
+ 💡
18
+ </div>
19
+ </div>
20
+
21
+ <div class="status-text" id="statusText">
22
+ Đèn đang tắt
23
+ </div>
24
+ </div>
25
+
26
+ <div class="controls">
27
+ <button class="btn btn-toggle" id="toggleBtn" onclick="toggleLight()">
28
+ Bật/Tắt Đèn
29
+ </button>
30
+ </div>
31
+ </div>
32
+
33
+ <script src="{{ url_for('static', filename='js/script.js') }}"></script>
34
+ </body>
35
+ </html>