R730XD IDRAC8 接入 Home Assistant(IPMI方式)
蜂窝煤 Lv3

HA容器安装IPMITool

我的 HA 是 Docker 容器安装的,先找到 HA 容器的 ID,SSH 连接到宿主机,输入 docker ps -a | grep homeassistant

1
2
3
<yourusername>@Docker:~# docker ps -a | grep homeassistant

9f0a5e8d7de3 ghcr.nju.edu.cn/hasscc/hacn:stable "/init" 3 days ago Up 28 hours (healthy) homeassistant

可以看到容器的 ID 为9f0a5e8d7de3,然后 docker exec -it 9f0a5e8d7de3 /bin/bash 进入容器的命令行。

然后根据 HA 基础镜像不同安装 IPMITool,Alpine 使用 apk add ipmitoolDebian/Ubuntu 使用 apt install -y ipmitool

1
2
3
4
5
6
7
8
9
10
11
Docker:/config# apk add ipmitool

fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz

fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz

(1/1) Installing ipmitool (1.8.19-r1)

Executing busybox-1.37.0-r12.trigger

OK: 184 MiB in 202 packages

安装好 IPMITool 后,测试一下ipmi的命令 ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> dcmi power reading (注意<>内的需要按你的实际情况填写),在 R730XD 上输出为:

1
2
3
4
5
6
Instantaneous power reading:                    12 Watts
Minimum during sampling period: 2 Watts
Maximum during sampling period: 296 Watts
Average power reading over sample period: 141 Watts
IPMI timestamp: 09/04/25 15:10:37 CST Sampling period: 00000001 Seconds.
Power reading state is: activated

编写IPMI命令

需要将什么数据接入HA,就需要自己手动写出查询这些数据的命令,下面给出我的供参考:

  • CPU温度(两个CPU中的最大值)
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> sdr elist full | awk -F'|' '/Temp *\| 0E|Temp *\| 0F/ {gsub(/degrees C/,"",$5); gsub(/ /,"",$5); if($5~/^[0-9]+$/ && $5>max) max=$5} END {if(max>0) print max; else print 0}'
  • 查询风扇转速(所有风扇转速平均值,单位RPM)
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> sdr elist full | grep "Fan" | awk -F'|' '{gsub(/RPM/, "", $5); gsub(/ /, "", $5); if($5 ~ /^[0-9]+$/) {sum+=$5; count++}} END {if(count>0) print sum/count; else print 0}'
  • 进气温度
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> sdr elist full | grep "Inlet Temp" | awk -F'|' '{print $5}' | grep -o '[0-9]\+'
  • 排气温度
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> sdr elist full | grep "Exhaust Temp" | awk -F'|' '{print $5}' | grep -o '[0-9]\+'
  • 功率
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> sdr elist full | awk -F'|' '/Pwr Consumption/ {gsub(/Watts/,"",$5); gsub(/ /,"",$5); print $5}'
  • 设置风扇自动调速(关)
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x01 0x00
  • 设置风扇自动调速(开)
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x01 0x01
  • 电源相关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查询电源状态
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P password chassis power status

# 开机
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P password chassis power on

# 软关机
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P password chassis power soft

# 重启
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P password chassis power cycle

# 重置
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P password chassis power reset
  • 设置风扇转速(%)

  • 10%

1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0xa
  • 20%
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x14
  • 100%
1
ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x64

接入HA

  1. 我这里使用command_line接入HA,打开HA的configuration.yaml ,加入下面的内容:
  • 记得替换 <yourIP><yourusername><yourpassword> 为你自己的值。

  • 我知道写set_fan_00set_fan_05…这种很蠢,但是我用模板语法实在调试不通,请大佬们有更加好的方案指教。

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
# R730XD 远程管理命令配置
shell_command:
r730xd_power_cycle: "ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> chassis power cycle"
r730xd_power_reset: "ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> chassis power reset"

# 风扇模式命令
set_fan_auto: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x01 0x01'
set_fan_manual: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x01 0x00'

# 预定义的风扇速度命令
set_fan_00: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x00'
set_fan_05: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x05'
set_fan_10: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x0a'
set_fan_15: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x0f'
set_fan_20: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x14'
set_fan_25: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x19'
set_fan_30: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x1e'
set_fan_35: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x23'
set_fan_40: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x28'
set_fan_45: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x2d'
set_fan_50: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x32'
set_fan_55: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x37'
set_fan_60: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x3c'
set_fan_65: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x41'
set_fan_70: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x46'
set_fan_75: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x4b'
set_fan_80: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x50'
set_fan_85: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x55'
set_fan_90: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x5a'
set_fan_95: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x5f'
set_fan_100: 'ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x64'

# R730XD 传感器和开关配置
command_line:
- sensor:
name: R730XD CPU Temp
command: "/config/scripts/ipmi_sdr_cache.sh cpu_temp"
unit_of_measurement: "°C"
command_timeout: 60
scan_interval: 30
value_template: "{{ value | int(0) }}"
icon: mdi:cpu-64-bit

- sensor:
name: R730XD Fans AVG RPM
command: "/config/scripts/ipmi_sdr_cache.sh fans_avg"
command_timeout: 60
scan_interval: 30
unit_of_measurement: "RPM"
value_template: "{{ value | int(0) }}"
icon: mdi:fan

- sensor:
name: R730XD Inlet Temp
command: "/config/scripts/ipmi_sdr_cache.sh inlet_temp"
unit_of_measurement: "°C"
command_timeout: 60
scan_interval: 30
value_template: "{{ value | int(0) }}"
icon: mdi:thermometer-chevron-up

- sensor:
name: R730XD Exhaust Temp
command: "/config/scripts/ipmi_sdr_cache.sh exhaust_temp"
command_timeout: 60
scan_interval: 30
unit_of_measurement: "°C"
value_template: "{{ value | int(0) }}"
icon: mdi:thermometer-chevron-down

- sensor:
name: R730XD Power
command: "/config/scripts/ipmi_sdr_cache.sh power"
command_timeout: 60
scan_interval: 30
unit_of_measurement: "W"
value_template: "{{ value | int(0) }}"
icon: mdi:flash

- switch:
name: R730XD Power Ctrl
command_on: "ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> chassis power on"
command_off: "ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> chassis power soft"
command_state: "ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> chassis power status | grep -qi 'on'"
command_timeout: 60
scan_interval: 30
icon: mdi:power

# R730XD 电源控制按钮配置
template:
- button:
- name: "R730XD Power Cycle"
icon: mdi:restart
press:
service: shell_command.r730xd_power_cycle

- name: "R730XD Power Reset"
icon: mdi:restart-alert
press:
service: shell_command.r730xd_power_reset

# R730XD 风扇控制配置
input_number:
fan_speed:
name: R730XD Fan Speed Ctrl
initial: 15
min: 0
max: 100
step: 5
mode: slider

script:
set_server_fan_speed:
alias: R730XD Set Fan Speed
sequence:
- variables:
fan_speed: "{{ speed | int }}"

# 设置风扇模式
- choose:
- conditions:
- condition: template
value_template: "{{ fan_speed == 0 }}"
sequence:
- service: shell_command.set_fan_auto
default:
- service: shell_command.set_fan_manual

- delay: '00:00:02'

# 设置风扇速度
- choose:
- conditions:
- condition: template
value_template: "{{ fan_speed == 0 }}"
sequence:
- service: shell_command.set_fan_00
- conditions:
- condition: template
value_template: "{{ fan_speed == 5 }}"
sequence:
- service: shell_command.set_fan_05
- conditions:
- condition: template
value_template: "{{ fan_speed == 10 }}"
sequence:
- service: shell_command.set_fan_10
- conditions:
- condition: template
value_template: "{{ fan_speed == 15 }}"
sequence:
- service: shell_command.set_fan_15
- conditions:
- condition: template
value_template: "{{ fan_speed == 20 }}"
sequence:
- service: shell_command.set_fan_20
- conditions:
- condition: template
value_template: "{{ fan_speed == 25 }}"
sequence:
- service: shell_command.set_fan_25
- conditions:
- condition: template
value_template: "{{ fan_speed == 30 }}"
sequence:
- service: shell_command.set_fan_30
- conditions:
- condition: template
value_template: "{{ fan_speed == 35 }}"
sequence:
- service: shell_command.set_fan_35
- conditions:
- condition: template
value_template: "{{ fan_speed == 40 }}"
sequence:
- service: shell_command.set_fan_40
- conditions:
- condition: template
value_template: "{{ fan_speed == 45 }}"
sequence:
- service: shell_command.set_fan_45
- conditions:
- condition: template
value_template: "{{ fan_speed == 50 }}"
sequence:
- service: shell_command.set_fan_50
- conditions:
- condition: template
value_template: "{{ fan_speed == 55 }}"
sequence:
- service: shell_command.set_fan_55
- conditions:
- condition: template
value_template: "{{ fan_speed == 60 }}"
sequence:
- service: shell_command.set_fan_60
- conditions:
- condition: template
value_template: "{{ fan_speed == 65 }}"
sequence:
- service: shell_command.set_fan_65
- conditions:
- condition: template
value_template: "{{ fan_speed == 70 }}"
sequence:
- service: shell_command.set_fan_70
- conditions:
- condition: template
value_template: "{{ fan_speed == 75 }}"
sequence:
- service: shell_command.set_fan_75
- conditions:
- condition: template
value_template: "{{ fan_speed == 80 }}"
sequence:
- service: shell_command.set_fan_80
- conditions:
- condition: template
value_template: "{{ fan_speed == 85 }}"
sequence:
- service: shell_command.set_fan_85
- conditions:
- condition: template
value_template: "{{ fan_speed == 90 }}"
sequence:
- service: shell_command.set_fan_90
- conditions:
- condition: template
value_template: "{{ fan_speed == 95 }}"
sequence:
- service: shell_command.set_fan_95
- conditions:
- condition: template
value_template: "{{ fan_speed == 100 }}"
sequence:
- service: shell_command.set_fan_100
default:
- service: system_log.write
data:
message: "不支持的风扇速度: {{ fan_speed }}%"
level: warning

automation:
- alias: R730XD Set Fan Speed on Change
trigger:
- platform: state
entity_id: input_number.fan_speed
condition:
- condition: template
value_template: "{{ trigger.from_state is not none }}"
- condition: template
value_template: "{{ trigger.from_state.state != trigger.to_state.state }}"
action:
- service: script.set_server_fan_speed
data:
speed: "{{ states('input_number.fan_speed') | int }}"
  1. config文件夹下新建一个文件夹scripts存放 Python 脚本:
  • 脚本的功能是创建一个tmp文件,每次将ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> sdr elist full的结果 + 时间戳写入这个文件,然后其他命令每次都先读取tmp文件,如果现在时间-文件时间戳 > 60s,则说明该缓存的TTL已经结束,再次执行ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> sdr elist full重新创建缓存,这样可以减少对ipmi的压力,解决命令经常无响应的问题。

  • 仍然要记得替换 <yourIP><yourusername><yourpassword> 为你自己的值。

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
#!/usr/bin/env bash
# ipmi_sdr_cache.sh
# Usage: ipmi_sdr_cache.sh <cpu_temp|fans_avg|inlet_temp|exhaust_temp|power|full>
# Place under /config/ and chmod +x it. Adjust HOST/USER/PASS or use secrets.

CACHE="/tmp/r730xd_sdr_cache"
TMP="$(mktemp /tmp/r730xd_sdr_cache.XXXXXX)"
HOST="<yourIP>"
USER="<yourusername>"
PASS="<yourpassword>"
TTL=30

IPMITOOL_CMD="ipmitool -I lanplus -H ${HOST} -U ${USER} -P ${PASS}"

timestamp() { date +%s; }

# ensure cache exists & fresh
now=$(timestamp)
filetime=0
if [ -f "$CACHE" ]; then
read -r filetime < "$CACHE" || filetime=0
fi

age=$((now - filetime))

if [ ! -f "$CACHE" ] || [ "$age" -gt "$TTL" ]; then
# try to fetch new data; write to temporary file then move atomically
if $IPMITOOL_CMD sdr elist full > "${TMP}.data" 2>/dev/null; then
echo "$now" > "${TMP}.tmp"
cat "${TMP}.data" >> "${TMP}.tmp"
mv -f "${TMP}.tmp" "$CACHE"
rm -f "${TMP}.data"
else
# ipmitool failed: if cache exists, we'll fall back to it; otherwise write a minimal cache
if [ ! -f "$CACHE" ]; then
echo "$now" > "$CACHE"
echo "NO_DATA_FROM_IPMITOOL" >> "$CACHE"
fi
fi
fi

# helper: get raw cached body (skip first line timestamp)
get_cached() {
if [ -f "$CACHE" ]; then
tail -n +2 "$CACHE"
else
echo ""
fi
}

case "$1" in
full)
get_cached
;;

cpu_temp)
# choose sensors matching 0E or 0F (same AWK logic as you had)
get_cached | awk -F'|' '/Temp *\| 0E|Temp *\| 0F/ {
gsub(/degrees C/,"",$5); gsub(/ /,"",$5);
if($5~/^[0-9]+$/ && $5>max) max=$5
} END { if(max>0) print max; else print 0 }'
;;

fans_avg)
get_cached | awk -F'|' '/Fan/ {
gsub(/RPM/,"",$5); gsub(/ /,"",$5);
if($5~/^[0-9]+$/){ sum+= $5; cnt++ }
} END {
if(cnt>0) {
avg = sum/cnt;
# print rounded int
printf "%d\n", avg
} else { print 0 }
}'
;;

inlet_temp)
# print first numeric from Inlet Temp
get_cached | awk -F'|' '/Inlet Temp/ {print $5}' | grep -o '[0-9]\+' | head -n1 | sed -e 's/^$/0/'
;;

exhaust_temp)
get_cached | awk -F'|' '/Exhaust Temp/ {print $5}' | grep -o '[0-9]\+' | head -n1 | sed -e 's/^$/0/'
;;

power)
get_cached | awk -F'|' '/Pwr Consumption/ {
gsub(/Watts/,"",$5); gsub(/ /,"",$5); print $5; exit
}' | sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//' | awk '{ if($0~/^[0-9]+$/) print $0; else print 0 }'
;;

*)
echo "Usage: $0 <cpu_temp|fans_avg|inlet_temp|exhaust_temp|power|full>"
exit 2
;;
esac

最后成果

image
 Comments
Comment plugin failed to load
Loading comment plugin