336 lines
12 KiB
Python
336 lines
12 KiB
Python
from datetime import datetime, timezone, timedelta, date
|
||
|
||
import pandas as pd
|
||
import requests
|
||
import json
|
||
|
||
from matplotlib import pyplot as plt
|
||
|
||
|
||
#登录接口认证
|
||
|
||
def loginAuth():
|
||
# 接口地址
|
||
global auth_token
|
||
url = "https://xj-tb.maimaiag.com/api/auth/login" # 请替换为实际地址
|
||
|
||
# 请求头
|
||
headers = {
|
||
"Content-Type": "application/json"
|
||
}
|
||
|
||
# 请求体数据
|
||
payload = {
|
||
"username": "read@xj.com",
|
||
"password": "maimai"
|
||
}
|
||
|
||
try:
|
||
# 发送POST请求
|
||
response = requests.post(
|
||
url,
|
||
headers=headers,
|
||
data=json.dumps(payload) # 或者直接使用 json=payload
|
||
)
|
||
|
||
# 检查响应状态码
|
||
if response.status_code == 200:
|
||
print("登录成功!")
|
||
print("响应数据:", response.json())
|
||
|
||
# 通常登录接口会返回token,可以这样获取
|
||
response_data = response.json()
|
||
auth_token = response_data.get("token") # 根据实际返回字段调整
|
||
if auth_token:
|
||
print("获取到的认证Token:", auth_token)
|
||
else:
|
||
print(f"登录失败,状态码: {response.status_code}")
|
||
print("错误信息:", response.text)
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
print(f"请求发生异常: {str(e)}")
|
||
except json.JSONDecodeError as e:
|
||
print(f"JSON解析错误: {str(e)}")
|
||
return auth_token
|
||
# 调用数据接口--历史数据
|
||
def getWeatherAndSoilData(auth_token,device_id,startTs,endTs,agg,interval,limit,orderBy,keys):
|
||
# 配置信息
|
||
BASE_URL = "https://xj-tb.maimaiag.com/" # 请替换为实际API地址
|
||
TOKEN = auth_token # 替换为实际的Bearer token
|
||
DEVICE_ID = device_id # 替换为实际的设备ID
|
||
# 构造请求URL
|
||
endpoint = f"/api/plugins/telemetry/DEVICE/{DEVICE_ID}/values/timeseries"
|
||
url = BASE_URL + endpoint
|
||
# 请求头
|
||
headers = {
|
||
"Content-Type": "application/json",
|
||
"X-Authorization": f"Bearer+{TOKEN}"
|
||
}
|
||
# 查询参数
|
||
params = {
|
||
"keys": keys, # 要查询的参数,多个用逗号分隔
|
||
"startTs": startTs, # 开始时间戳(毫秒)- 可选
|
||
"endTs": endTs, # 结束时间戳(毫秒)- 可选
|
||
"agg": agg, # 聚合函数 - 可选
|
||
"interval": interval, # 聚合间隔(毫秒)- 可选
|
||
"limit": limit, # 返回条数 - 可选
|
||
"orderBy": orderBy # 排序方式 - 可选
|
||
}
|
||
try:
|
||
# 发送GET请求
|
||
response = requests.get(
|
||
url,
|
||
headers=headers,
|
||
params=params
|
||
)
|
||
# 检查响应状态码
|
||
if response.status_code == 200:
|
||
# print("请求成功!")
|
||
data = response.json()
|
||
# print("响应数据:", json.dumps(data, indent=2))
|
||
converted_data = {k: convert_timestamp_to_datetime(v) for k, v in data.items()}
|
||
return converted_data
|
||
elif response.status_code == 401:
|
||
print("认证失败,请检查Token是否正确")
|
||
elif response.status_code == 404:
|
||
print("设备不存在或路径错误")
|
||
else:
|
||
print(f"请求失败,状态码: {response.status_code}")
|
||
print("错误信息:", response.text)
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
print(f"请求发生异常: {str(e)}")
|
||
except json.JSONDecodeError as e:
|
||
print(f"JSON解析错误: {str(e)}")
|
||
print("原始响应:", response.text)
|
||
def convert_timestamp_to_datetime(data_dict):
|
||
"""
|
||
递归地将字典中的所有'timestamp'字段转换为可读的日期时间格式
|
||
保留原始时间戳的同时添加新的'datetime'字段
|
||
"""
|
||
if isinstance(data_dict, dict):
|
||
if "ts" in data_dict:
|
||
# 转换毫秒时间戳为datetime对象
|
||
timestamp_sec = data_dict["ts"] / 1000
|
||
dt = datetime.fromtimestamp(timestamp_sec)
|
||
# 添加新字段,保留原始时间戳
|
||
data_dict["datetime"] = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||
return data_dict
|
||
elif isinstance(data_dict, list):
|
||
return [convert_timestamp_to_datetime(item) for item in data_dict]
|
||
else:
|
||
return data_dict
|
||
|
||
def dateToTimestamp(date):
|
||
# dt = datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
|
||
if isinstance(date, datetime):
|
||
# 如果已经是datetime对象,直接使用
|
||
dt = date
|
||
elif isinstance(date, str):
|
||
# 如果是字符串,解析为datetime对象
|
||
dt = datetime.strptime(date, "%Y-%m-%d %H:%M:%S.%f")
|
||
else:
|
||
raise TypeError("date参数必须是字符串或datetime对象")
|
||
timestamp_ms = int(dt.timestamp() * 1000)
|
||
return timestamp_ms
|
||
|
||
#调用接口获取未来气象数据
|
||
def getFutureWeather(monitor_date):
|
||
base_url_future="https://api.open-meteo.com/"
|
||
end_point="v1/forecast"
|
||
current_date = datetime.now().date()
|
||
params = {
|
||
"latitude": "41.34252110189814", # 纬度
|
||
"longitude": "86.28810755462379", # 经度
|
||
"hourly": "temperature_2m,precipitation,precipitation_probability"
|
||
}
|
||
# if monitor_date.date()<current_date:
|
||
# # url = base_url_history + end_point
|
||
# params["start_date"] = (monitor_date + timedelta(days=1)).strftime("%Y-%m-%d")
|
||
# params["end_date"] = (monitor_date + timedelta(days=3)).strftime("%Y-%m-%d")
|
||
#
|
||
# else:
|
||
url = base_url_future + end_point
|
||
params["forecast_days"]= 4
|
||
|
||
try:
|
||
# 发送GET请求
|
||
response = requests.get(
|
||
url,
|
||
params=params
|
||
)
|
||
# 检查响应状态码
|
||
if response.status_code == 200:
|
||
data = response.json()
|
||
result={}
|
||
if monitor_date.date()<current_date:
|
||
result["qw_day1"] = get_tjvalue(data['hourly']['temperature_2m'][:24])
|
||
result["qw_day2"] = get_tjvalue(data['hourly']['temperature_2m'][24:48])
|
||
result["qw_day3"] = get_tjvalue(data['hourly']['temperature_2m'][48:72])
|
||
result["js_day1"] = get_tjvalue(data['hourly']['precipitation'][:24])
|
||
result["js_day2"] = get_tjvalue(data['hourly']['precipitation'][24:48])
|
||
result["js_day3"] = get_tjvalue(data['hourly']['precipitation'][48:72])
|
||
else:
|
||
result["qw_day1"] = get_tjvalue(data['hourly']['temperature_2m'][24:48])
|
||
result["qw_day2"] = get_tjvalue(data['hourly']['temperature_2m'][48:72])
|
||
result["qw_day3"] = get_tjvalue(data['hourly']['temperature_2m'][72:96])
|
||
result["js_day1"] = get_tjvalue(data['hourly']['precipitation'][24:48])
|
||
result["js_day2"] = get_tjvalue(data['hourly']['precipitation'][48:72])
|
||
result["js_day3"] = get_tjvalue(data['hourly']['precipitation'][72:96])
|
||
return result
|
||
elif response.status_code == 401:
|
||
print("认证失败,请检查Token是否正确")
|
||
elif response.status_code == 404:
|
||
print("设备不存在或路径错误")
|
||
else:
|
||
print(f"请求失败,状态码: {response.status_code}")
|
||
print("错误信息:", response.text)
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
print(f"请求发生异常: {str(e)}")
|
||
except json.JSONDecodeError as e:
|
||
print(f"JSON解析错误: {str(e)}")
|
||
print("原始响应:", response.text)
|
||
def get_tjvalue(data):
|
||
result={}
|
||
max_val = max(data)
|
||
min_val = min(data)
|
||
mean_val = sum(data) / len(data)
|
||
sum_val = sum(data)
|
||
result['max']=max_val
|
||
result['min']=min_val
|
||
result['mean']=mean_val
|
||
result['sum'] = sum_val
|
||
return result
|
||
soil_params={
|
||
"1号传感器":{
|
||
"device_id":"31fbb2d0-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"2号传感器": {
|
||
"device_id": "3bf4cc90-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"3号传感器": {
|
||
"device_id": "43a57cf0-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"4号传感器": {
|
||
"device_id": "4ff6b780-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"5号传感器": {
|
||
"device_id": "598c5480-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"6号传感器": {
|
||
"device_id": "6ff07260-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"7号传感器": {
|
||
"device_id": "8637b970-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"8号传感器": {
|
||
"device_id": "955de5f0-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"9号传感器": {
|
||
"device_id": "a24e94d0-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"10号传感器": {
|
||
"device_id": "adb7e060-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"11号传感器": {
|
||
"device_id": "b74c6bf0-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
"12号传感器": {
|
||
"device_id": "c0f36e10-561b-11f0-a556-4f10f26fc07f"
|
||
},
|
||
|
||
}
|
||
def get_soil_data_dk1(auth_token,startTs, endTs):
|
||
keys_soil = "SOIL_MOISTURE_SURFACE,SOIL_MOISTURE_MIDDLE,SOIL_MOISTURE_BOTTOM"
|
||
limit = ""
|
||
orderBy = ""
|
||
result={}
|
||
for key in soil_params:
|
||
id=soil_params[key]['device_id']
|
||
soil_data = getWeatherAndSoilData(auth_token,id, startTs, endTs, "AVG", 3600000, limit, orderBy,
|
||
keys_soil)
|
||
result[key]=soil_data
|
||
return result
|
||
def get_soil_data_dk2(auth_token,device_id,startTs, endTs):
|
||
keys_soil = "SOIL_MOISTURE_SURFACE,SOIL_MOISTURE_MIDDLE,SOIL_MOISTURE_BOTTOM"
|
||
limit = ""
|
||
orderBy = ""
|
||
result={}
|
||
soil_data = getWeatherAndSoilData(auth_token,device_id, startTs, endTs, "AVG", 3600000, limit, orderBy,
|
||
keys_soil)
|
||
result[device_id]=soil_data
|
||
return result
|
||
weather_params={
|
||
"device_id" : "18d121f0-561b-11f0-a556-4f10f26fc07f",
|
||
"agg" :"AVG",
|
||
"interval" : 3600000, #1小时
|
||
"limit" :"",
|
||
"orderBy" : "",
|
||
"keys":"AIR_TEMPERATURE,WIND_SPEED,AIR_LUX,AIR_HUMIDITY,RAIN_FALL_REALTIME"
|
||
}
|
||
def get_plot(data_list):
|
||
df = pd.DataFrame(data_list)
|
||
df['value'] = pd.to_numeric(df['value'], errors='coerce').round(3)# 转换湿度值为浮点数
|
||
df['datetime'] = pd.to_datetime(df['datetime']) # 转换为datetime类型
|
||
|
||
# 设置画布大小
|
||
plt.figure(figsize=(12, 6))
|
||
|
||
# 绘制折线图
|
||
plt.plot(df['datetime'], df['value'],
|
||
color='teal',
|
||
linestyle='-',
|
||
linewidth=2,
|
||
marker='o',
|
||
markersize=4,
|
||
markerfacecolor='orange',
|
||
markeredgecolor='orange',
|
||
label='Soil Moisture (%)')
|
||
|
||
# 添加22点后的数据高亮(符合你之前获取夜间数据的需求)
|
||
night_data = df[df['datetime'].dt.hour >= 22]
|
||
plt.scatter(night_data['datetime'], night_data['value'],
|
||
color='red',
|
||
s=30,
|
||
label='22:00-06:00 Data')
|
||
|
||
# 设置标题和坐标轴标签
|
||
plt.title('Soil Moisture Time Series (2025-06-22 to 2025-06-30)',
|
||
fontsize=14,
|
||
pad=15)
|
||
plt.xlabel('Datetime', fontsize=12, labelpad=10)
|
||
plt.ylabel('Soil Moisture (%)', fontsize=12, labelpad=10)
|
||
|
||
# 设置x轴刻度旋转,避免重叠
|
||
plt.xticks(rotation=45, ha='right', fontsize=10)
|
||
|
||
# 添加网格线
|
||
plt.grid(axis='y', linestyle='--', alpha=0.7)
|
||
|
||
# 添加图例
|
||
plt.legend(loc='upper right', fontsize=10)
|
||
|
||
# 调整布局
|
||
plt.tight_layout()
|
||
|
||
# 显示图表
|
||
plt.show()
|
||
if __name__ == "__main__":
|
||
auth_token = loginAuth()
|
||
device_id="18d121f0-561b-11f0-a556-4f10f26fc07f"
|
||
agg="AVG"
|
||
interval=21600000
|
||
limit=""
|
||
orderBy=""
|
||
keys="RAIN_FALL_REALTIME"
|
||
date_start = "2025-06-25 00:00:00.123"
|
||
date_end = "2025-07-01 23:59:59.123"
|
||
startTs=dateToTimestamp(date_start)
|
||
endTs=dateToTimestamp(date_end)
|
||
# data=get_soil_data_dk1(auth_token,startTs,endTs)
|
||
# print(data)
|
||
data=getWeatherAndSoilData(auth_token,device_id,startTs,endTs,"AVG",3600000,limit,orderBy,keys)
|
||
print(data['RAIN_FALL'])
|
||
# get_plot(data['1号传感器']['SOIL_MOISTURE_SURFACE'])
|
||
get_plot(data['RAIN_FALL_REALTIME']) |