R730XD IDRAC8 接入 Home Assistant(IPMI方式)
我的 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 ipmitool,Debian/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,就需要自己手动写出查询这些数据的命令,下面给出我的供参考:
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}'
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
1 ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0xa
1 ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x14
1 ipmitool -I lanplus -H <yourIP> -U <yourusername> -P <yourpassword> raw 0x30 0x30 0x02 0xff 0x64
接入HA
我这里使用command_line接入HA,打开HA的configuration.yaml ,加入下面的内容:
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 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' 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 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 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 }} "
在 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
最后成果