物联网・盛世宏博・SNMP、Python、机房动环、温湿度记录仪、批量采集
关键词:Python SNMP、机房动环监控、温湿度记录仪、批量采集、pysnmp、SNMP v2c、OID、机房温湿度采集、动环开发、物联网采集脚本 标签:Python开发、SNMP采集、机房动环、温湿度传感器、批量轮询、运维脚本、动环监控开发

在机房、档案馆库房、配电室动环项目中,大量温湿度记录仪、RJ45网口温湿度传感器普遍支持SNMP v2c协议。很多项目需要快速做采集原型、对接自有平台、做本地日志存储,不想直接采购成套动环采集软件。
SNMP(简单网络管理协议)是工业传感器、动环设备非常通用的采集协议,大部分国产以太网温湿度记录仪默认开启SNMP v2c,配置好共同体字(community)、设备IP,给定温度OID、湿度OID即可读取实时数值。
本文手撕Python完整采集代码,基于pysnmp实现批量多IP并发轮询,读取温湿度,支持超时控制、异常捕获、结果输出、写入本地日志,适合快速验证设备、做采集Demo、二次集成到机房动环、智慧档案监控系统。
适用设备:支持SNMP v2c的RJ45温湿度记录仪(很多动环传感器默认OID:温度
.1.3.6.1.4.1.xxxx.1.1.1,湿度.1.3.6.1.4.1.xxxx.1.1.2,不同厂商OID有差异,需要看设备手册) 不适用:仅支持Modbus TCP、RS485的传感器。
我们使用 pysnmp 库,支持SNMP v1/v2c/v3,代码兼容Python3.7~3.11
pip install pysnmp注意:pysnmp版本建议使用 4.4.12 稳定版,新版本API改动较大。
pip install pysnmp==4.4.12public);常见坑:SNMP是UDP协议,不是TCP,ping通不代表161端口通;交换机ACL、设备白名单会直接导致超时无返回。
可以用 snmpwalk 命令先行验证(需要安装net-snmp工具)
snmpwalk -v2c -c public 192.168.1.100 .1.3.6.1.4.1.xxx能看到返回温度、湿度数值,说明OID和网络正常,再跑Python脚本。
这是底层封装函数,输入IP、community、oid,返回原始值,增加超时、重试、异常捕获。
from pysnmp.hlapi import *
import time
def snmp_get(ip: str, community: str, oid: str, timeout=1, retries=1):
"""
SNMP v2c GET 单OID读取
:param ip: 设备IP
:param community: 读共同体,一般public
:param oid: 对象标识符字符串,如 ".1.3.6.1.4.1.xx.1.1.1"
:param timeout: 超时时间 单位s
:param retries: 重试次数
:return: 成功返回数值,失败返回None
"""
errorIndication, errorStatus, errorIndex, varBinds = next(
getCmd(SnmpEngine(),
CommunityData(community, mpModel=1), # mpModel=1 → SNMPv2c
UdpTransportTarget((ip, 161), timeout=timeout, retries=retries),
ContextData(),
ObjectType(ObjectIdentity(oid)))
)
if errorIndication:
# 超时、不可达、共同体错误都会走到这里
print(f"[SNMP ERROR] {ip} | {errorIndication}")
return None
elif errorStatus:
print(f"[SNMP ERROR] {ip} | {errorStatus.prettyPrint()} @ {errorIndex}")
return None
else:
for varBind in varBinds:
raw_val = varBind[1].prettyPrint()
return raw_val测试调用示例:
if __name__ == "__main__":
temp_oid = ".1.3.6.1.4.1.2021.10.1.1"
hum_oid = ".1.3.6.1.4.1.2021.10.1.2"
ip_addr = "192.168.1.100"
com = "public"
t_val = snmp_get(ip_addr, com, temp_oid)
h_val = snmp_get(ip_addr, com, hum_oid)
print(f"IP:{ip_addr} 温度:{t_val} 湿度:{h_val}")很多国产温湿度传感器返回值是放大10倍的整数,例如返回256代表25.6℃,后续代码里会增加换算逻辑。
串行轮询大量设备速度很慢,这里使用concurrent.futures.ThreadPoolExecutor多线程并发批量抓取,适合几十台机房温湿度记录仪同时采集;增加IP清单配置、数值换算、结果打印、日志写入本地csv。
说明:SNMP本身是IO密集型任务,多线程非常适合,不要开超大并发,建议线程池大小20以内,防止UDP报文风暴。
from pysnmp.hlapi import *
from concurrent.futures import ThreadPoolExecutor, as_completed
import csv
import time
from datetime import datetime
# ====================== 【配置区,根据现场修改】 ======================
COMMUNITY = "public"
TEMP_OID = ".1.3.6.1.4.1.37244.1.1.1" # 替换为你设备温度OID
HUM_OID = ".1.3.6.1.4.1.37244.1.1.2" # 替换为你设备湿度OID
SCALE = 10.0 # 很多传感器数值放大10倍,除以10得到真实值;如果是直接浮点返回设置为1
TIMEOUT = 1.2
MAX_WORKERS = 15 # 并发线程数量
# 批量设备IP清单,在这里录入所有机房温湿度记录仪IP列表
DEVICE_LIST = [
{"ip":"192.168.1.100","name":"机房A-01"},
{"ip":"192.168.1.101","name":"机房A-02"},
{"ip":"192.168.1.102","name":"配电室-01"},
{"ip":"192.168.1.103","name":"库房-01"},
]
LOG_FILE = "snmp_temp_hum_log.csv"
# ====================================================================
def snmp_get_one_oid(ip, community, oid):
errorIndication, errorStatus, errorIndex, varBinds = next(
getCmd(SnmpEngine(),
CommunityData(community, mpModel=1),
UdpTransportTarget((ip, 161), timeout=TIMEOUT, retries=1),
ContextData(),
ObjectType(ObjectIdentity(oid)))
)
if errorIndication or errorStatus:
return None
for varBind in varBinds:
return varBind[1].prettyPrint()
def task_read_device(dev):
"""单个设备采集任务:读取温度、湿度,换算、返回字典"""
ip = dev["ip"]
dev_name = dev["name"]
t_raw = snmp_get_one_oid(ip, COMMUNITY, TEMP_OID)
h_raw = snmp_get_one_oid(ip, COMMUNITY, HUM_OID)
ret = {
"name": dev_name,
"ip": ip,
"temp": None,
"hum": None,
"ts": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
try:
if t_raw is not None:
ret["temp"] = round(float(t_raw)/SCALE, 2)
if h_raw is not None:
ret["hum"] = round(float(h_raw)/SCALE, 2)
except Exception as e:
print(f"数据解析异常 {ip}: {str(e)}")
return ret
def batch_collect():
"""批量并发采集入口"""
result_list = []
print(f"==== 开始批量SNMP采集,设备总数:{len(DEVICE_LIST)} ====")
start = time.time()
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
future_map = {executor.submit(task_read_device, dev): dev for dev in DEVICE_LIST}
for future in as_completed(future_map):
res = future.result()
result_list.append(res)
cost = time.time() - start
print(f"==== 采集完成,耗时 {cost:.2f}s ====")
return result_list
def save_to_csv(data_list):
"""追加写入CSV日志,用于历史归档,对接后续数据分析"""
header = ["ts","name","ip","temp","hum"]
with open(LOG_FILE, "a", encoding="utf-8-sig", newline="") as f:
writer = csv.DictWriter(f, fieldnames=header)
# 判断文件是否为空,为空则写表头
f.seek(0,2)
if f.tell() == 0:
writer.writeheader()
for item in data_list:
writer.writerow(item)
if __name__ == "__main__":
while True:
collect_result = batch_collect()
# 控制台打印结果
for item in collect_result:
print(f"[{item['ts']}] {item['name']:>10} | {item['ip']:>15} | 温度:{item['temp']} ℃ | 湿度:{item['hum']} %RH")
save_to_csv(collect_result)
time.sleep(30) # 采集间隔30s,按需修改SCALE 参数适配“放大10倍整型返回”的国产温湿度记录仪,是这类设备最常见的格式;name+ip管理,方便机房、档案库房、配电室按点位命名。这份脚本是采集底座,可以按需二次开发:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。