-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsend.py
More file actions
executable file
·342 lines (292 loc) · 15 KB
/
Copy pathsend.py
File metadata and controls
executable file
·342 lines (292 loc) · 15 KB
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# -*- coding: utf-8 -*-
import json
import requests
import uuid
import os
from datetime import datetime
import time
from dotenv import load_dotenv
from ai_client import ask_ai
if os.path.exists(".env"): load_dotenv()
USER_FILE = r"json/users.json"
KEEP_FILE = r"json/keep.json"
DATA_FILE = r"json/data.json"
LOG_FILE = r"json/log.json"
SECRET_TOKEN = os.getenv('SECRET_TOKEN')
TEXT = "GripMind 是什麼的答案是 GripMind 是一款結合物聯網(IoT)與智慧互動提醒功能的手部肌力復健裝置,能即時測量握力並提供每日數據回饋。GripMind 的功能有哪些的答案是 GripMind 擁有三大功能:智慧握力測量、即時 LINE 推播提醒,以及多模式手部訓練設計,能依不同族群需求提供個人化復健方案。GripMind 使用了哪些感測器的答案是 本裝置使用 HX711 壓力感測模組搭配應變規(strain gauge),透過非線性數學模型轉換為握力重量(公斤數)進行即時監測。GripMind 的資料會儲存在哪裡的答案是 所有使用者的握力紀錄會以 JSON 檔案儲存於本地端伺服器,包含 data.json(握力資料)與 users.json(裝置與 LINE 使用者綁定資訊)。裝置的提醒功能怎麼運作的答案是 系統每天會在固定時間(18:01)自動檢查是否上傳握力資料,若未達成,將透過 LINE Bot 自動推播提醒使用者進行測量。GripMind 的訓練模式有哪些的答案是 GripMind 提供多種訓練模式,包括輕壓復健、靜態握持訓練與進階挑戰訓練,未來亦可透過 LINE 設定模式以因應不同需求。這套裝置是給誰用的答案是 本裝置主要針對高齡長者、術後復健者與行動不便者設計,也適合需進行手部肌肉訓練的族群,如運動員或長期久坐者。為什麼握力訓練對失智有幫助的答案是 研究顯示握力與認知功能具高度正相關,透過穩定的手部肌力訓練可有效減緩腦部退化、降低失智與中風等風險。GripMind 的成本與售價大概是多少的答案是 裝置預估成本為 519.5 元,含塑膠外殼、感測器、LCD 顯示器等,預計依三倍定價法設定建議售價為新台幣 1800 元。這套系統的後端是怎麼設計的答案是 GripMind 系統使用 Python Flask 框架作為後端伺服器,提供握力數據接收 API,整合 LINE Messaging API 推播功能,並儲存資料於本地 JSON 檔案中。GripMind 可以記錄每天的握力變化嗎的答案是 是的,GripMind 會自動紀錄每日握力數值,使用者可以透過圖表或資料回顧自己每週、每月的訓練變化趨勢。GripMind 怎麼把感測器的數值轉換成公斤的答案是 裝置使用非線性曲線擬合公式將壓力感測器回傳的電阻值轉換為公斤數,精準度經實測驗證可應用於復健場域。LINE Bot 的提醒會每天發送嗎的答案是 系統每天固定於 18:01 檢查是否上傳握力資料,若尚未完成訓練,將由 LINE Bot 自動提醒使用者。這套裝置是否有 APP 的答案是 目前 GripMind 的使用介面以 LINE Bot 與網頁介面為主,未來將視需求推出手機 App 版本以強化互動與紀錄功能。這個產品可以商業化嗎的答案是 GripMind 已進行市場競爭力分析與成本試算,具備量產潛力,未來可導入長照機構、家庭照護或健康保險產業中。可以多台裝置連接一個 LINE 嗎的答案是 目前一個 LINE 使用者綁定一個裝置,但系統結構具備擴充性,未來可支援多裝置對一帳號的資料分辨與管理。GripMind 和市售握力器有什麼不同的答案是 市售握力器多以運動訓練為導向,無數據回饋;GripMind 主打復健照護與智慧提醒,支援低強度握力、資料紀錄與目標追蹤。資料是儲存在雲端還是本地的答案是 目前資料儲存在本地端 JSON 檔案中,未來將考慮導入雲端資料庫以支援跨裝置同步與遠端資料備份。使用 GripMind 時需要電源嗎的答案是 是的,GripMind 裝置需外部供電,可透過行動電源或 USB 接口供電,亦可根據需求改裝電池模組。我可以設定自己的每日訓練目標嗎的答案是 目前系統預設目標值為開發者指定數值,後續版本將開放使用者在 LINE 中自定訓練強度與目標。"
def read_json_file(path, default_value):
if not os.path.exists(path):
return default_value
try:
with open(path, "r", encoding="utf8") as f:
return json.load(f)
except json.JSONDecodeError:
return default_value
class Keep():
"""讀取各 json 中的資訊"""
@staticmethod
def logs():
return read_json_file(LOG_FILE, [])
@staticmethod
def datas():
return read_json_file(DATA_FILE, [])
def send_push_message(user_id, messages):
"""發送打包好的訊息給指定使用者"""
url = "https://api.line.me/v2/bot/message/push"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {str(os.getenv('MESSAGING_API_ACCESS_TOKEN'))}",
"X-Line-Retry-Key": str(uuid.uuid4())
}
payload = {
"to": user_id,
"messages": messages
}
save_log(f"Have allready send {messages} to {user_id}")
response = requests.post(url, headers=headers, json=payload)
return response.status_code, response.text
def daily_check_task():
"""每天18:01檢查所有使用者今日有無更新資料"""
while True:
now = datetime.now()
time.sleep(5)
if now.hour == 18 and now.minute == 1:
save_log("正在執行每日資料檢查...")
try:
with open(USER_FILE, "r", encoding="utf-8") as f:
users = json.load(f)
except Exception as e:
save_log(f"讀取使用者失敗: {e}")
users = []
try:
with open(DATA_FILE, "r", encoding="utf-8") as f:
grip_data = json.load(f)
except Exception as e:
save_log(f"讀取握力資料失敗: {e}")
grip_data = []
today = datetime.now().date()
for user in users:
user_id = user.get("userId")
device_id = user.get("deviceId")
found_today = False
for record in grip_data:
if record.get("device_id") == device_id:
file_time = datetime.fromtimestamp(
os.path.getmtime(DATA_FILE)
).date()
if file_time == today:
found_today = True
break
if not found_today:
msg = {
"type": "text",
"text": "⚠️ 你今天還沒有上傳握力資料喔,記得量測一下吧!"
}
status, response = send_push_message(user_id, [msg])
save_log(f"已提醒 {user_id}:{status}, {response}")
time.sleep(61)
else:
time.sleep(30)
def check_done_the_goal(goal, grip_value):
"""檢查有無達到目標公斤數"""
if grip_value >= goal:
return f"你已完成今日目標({goal}kg),繼續加油!"
else:
return f"你已經很努力了,明天再接再厲! (目標:{goal}kg)"
def save_grip_data(deviceid, grip_value):
"""儲存握力資料進 json/data.json,保留歷史數據,每次都附加新紀錄"""
# 嘗試讀取既有紀錄
records = []
if os.path.exists(DATA_FILE):
with open(DATA_FILE, "r", encoding="utf-8") as f:
try:
records = json.load(f)
except json.JSONDecodeError:
save_log("data.json 檔案格式錯誤,將重新初始化")
records = []
# 新增這次的握力紀錄
new_record = {
"device_id": deviceid,
"grip": grip_value,
"timestamp": time.strftime("%Y%m%d %H:%M:%S")
}
records.append(new_record)
# 將所有紀錄寫回檔案
try:
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(records, f, ensure_ascii=False, indent=2)
return "資料儲存成功"
except Exception as e:
return f"資料儲存失敗:{e}"
def send_grip_data(device_id, grip_value):
"""根據裝置 ID 發送握力訊息給對應 userId"""
try:
with open(USER_FILE, "r", encoding="utf-8") as f:
try:
users = json.load(f)
except json.JSONDecodeError:
save_log({"error": "使用者資料格式錯誤(可能為空檔)"})
return {"error": "使用者資料格式錯誤(可能為空檔)"}, 500
except FileNotFoundError:
save_log({"error": "找不到使用者資料"})
return {"error": "找不到使用者資料"}, 500
target = next((u for u in users if u.get("deviceId") == device_id), None)
if not target:
save_log({"error": f"找不到對應的裝置 ID: {device_id}"})
return {"error": f"找不到對應的裝置 ID: {device_id}"}, 404
user_id = target["userId"]
goal = float(target["target"])
message = {
"type": "text",
"text": f"今日握力紀錄:{grip_value} kg,{check_done_the_goal(goal, grip_value)}"
}
status, response = send_push_message(user_id, [message])
save_result = save_grip_data(device_id, grip_value)
save_log({"message": f"已發送給 {user_id}:{status}, {response}, 資料庫儲存狀況:{save_result}"})
return {"message": f"已發送給 {user_id}:{status}, {response}, 資料庫儲存狀況:{save_result}"}, 200
def get_device_id():
"""把users.json中所有的device id讀出來"""
devices = []
if os.path.exists(USER_FILE):
with open(USER_FILE, "r", encoding="utf-8") as f:
users = json.load(f)
else:
users = []
for i in users:
devices.append(i['deviceId'])
return devices
def save_user_device(user_id, device_id, age, gender, condition, method):
"""新增或更新userid及其對應的deviceid"""
NEW = False
if os.path.exists(USER_FILE):
with open(USER_FILE, "r", encoding="utf-8") as f:
users = json.load(f)
else:
users = []
for user in users:
if user["userId"] == user_id:
user["deviceId"] = device_id
break
else:
NEW = True
suggest_target = suggest(condition, gender)
if user_id == "U19f5c7ea1eb5591d7d374a4a62374f0f": # 開發者測試帳號
users.append({"userId": user_id,
"deviceId": device_id,
"target": suggest_target,
"age": age,
"gender": gender,
"condition": condition,
"method": method,
"points": 1000000000000})
else:
users.append({"userId": user_id,
"deviceId": device_id,
"target": suggest_target,
"age": age,
"gender": gender,
"condition": condition,
"method": method,
"points": 100})
with open(USER_FILE, "w", encoding="utf-8") as f:
json.dump(users, f, indent=4, ensure_ascii=False)
if NEW:
return suggest_target
else:
return user["target"]
def get_user_points(user_id):
"""取得使用者剩餘點數"""
if os.path.exists(USER_FILE):
with open(USER_FILE, "r", encoding="utf-8") as f:
users = json.load(f)
else:
return 0
for user in users:
if user["userId"] == user_id:
return user.get("points", 0)
return 0
def deduct_user_points(user_id, points_to_deduct):
"""扣除使用者點數"""
if os.path.exists(USER_FILE):
with open(USER_FILE, "r", encoding="utf-8") as f:
users = json.load(f)
else:
return False, "找不到使用者檔案"
user_found = False
for user in users:
if user["userId"] == user_id:
user_found = True
if user.get("points", 0) >= points_to_deduct:
user["points"] -= points_to_deduct
with open(USER_FILE, "w", encoding="utf-8") as f:
json.dump(users, f, indent=4, ensure_ascii=False)
return True, "點數扣除成功"
else:
return False, "點數不足"
if not user_found:
return False, "找不到使用者"
def suggest(condition, gender):
"""查表並給出建議目標公斤數"""
recommendation = {
'Trigger finger': {
'female': 1.9,
'male': 2.9
},
'Basal joint arthritis': {
'female': 3.7,
'male': 4.3
},
"De Quervain's disease": {
'female': 4.3,
'male': 5.0
}
}
try:
return recommendation[condition][gender]
except KeyError:
return 3.0
def change_target_value(device_id, target_value):
"""改變users.json中的目標公斤數"""
if os.path.exists(USER_FILE):
with open(USER_FILE, "r", encoding="utf-8") as f:
users = json.load(f)
else:
return "查無此裝置", 404
for user in users:
if user["deviceId"] == device_id:
user["target"] = target_value
break
else:
return "查無此裝置", 404
with open(USER_FILE, "w", encoding="utf-8") as f:
json.dump(users, f, indent=4, ensure_ascii=False)
return "成功", 200
def get_user_information(device_id):
"""取得該device_id對應的所有資料"""
if os.path.exists(USER_FILE):
with open(USER_FILE, "r", encoding="utf-8") as f:
users = json.load(f)
else:
return "查無此裝置", 404
for user in users:
if user["deviceId"] == device_id:
return [user["target"], user["age"], user["gender"], user["condition"], user["method"], user.get("points", 0)], 200 # 新增回傳點數
else:
return "查無此裝置", 404
def save_log(text):
with open(LOG_FILE, "r", encoding="utf8") as f:
logs = json.load(f)
logs.append({"time": time.ctime(time.time()), "log": text})
with open(LOG_FILE, "w", encoding="utf8") as f:
json.dump(logs, f, indent=4, ensure_ascii=False)
def replay_msg(user_msg):
"""Line自動回覆訊息"""
return ask_ai(f"請先閱讀下列資料再回答「{user_msg}」,我要3句話內的純文字,用台灣人會用的繁體中文。如果問題在資料中找不到答案,就用風趣的方式回答吧,可以不需要按照資料來回答\n\n {TEXT}")
def clean_users():
"""清除使用者資料"""
with open(USER_FILE, "w", encoding="utf-8") as f:
json.dump([], f, indent=4)
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump([], f, indent=4)
if __name__ == "__main__":
print("這裡是自建函式庫,你點錯了,請使用 app.py 發送資料測試")