Proxmox VE 主机与虚拟机控制卡片

先上一张效果图

这个 Home Assistant 仪表盘卡片用于监控和控制 Proxmox VE (PVE) 主机及其虚拟机状态。通过精心设计的按钮卡片,可以直观显示运行状态并执行开关机操作。

🛠 前置要求

  1. Proxmox VE 集成

    • 需安装 Proxmox VE 集成(通过 HACS 或手动配置)

    • 集成会自动创建以下类型实体:

      • binary_sensor.[vm_id]_status(虚拟机状态)

      • sensor.[vm_id]_node(虚拟机所在节点)

      • button.[vm_id]_start(开机按钮)

      • button.[vm_id]_shutdown(关机按钮)

  2. Button Card 自定义卡片

    yaml

    # 通过 HACS 安装
    resources:
      - url: /hacsfiles/button-card/button-card.js
        type: module
  3. Horizontal Stack 布局

    yaml

    # 在 Lovelace 配置中添加
    resources:
      - url: /hacsfiles/layout-card/layout-card.js
        type: module

🧩 卡片配置解析

整体结构

yaml

type: horizontal-stack  # 水平堆叠布局
cards:
  - type: custom:button-card  # PVE主机卡片
    entity: binary_sensor.node_home_status
    ...
  - type: custom:button-card  # Windows11虚拟机卡片
    entity: binary_sensor.qemu_windows11_100_status
    ...
  # 其他虚拟机卡片...

🔘 PVE 主机卡片(核心逻辑)

yaml

- type: custom:button-card
  entity: binary_sensor.node_home_status
  name: PVE
  tap_action:
    action: call-service
    service: |-
      [[[ 
        // 动态服务选择
        entity.state === 'on' ? 'button.press' : 'switch.turn_on'
      ]]]
    service_data:
      entity_id: |-
        [[[
          // 动态实体选择
          entity.state === 'on' 
            ? 'button.node_home_shutdown'   # 关机按钮
            : 'switch.pve_switch'           # 开机开关
        ]]]
    confirmation: |-
      [[[
        // 仅关机时显示确认弹窗
        entity.state === 'on' ? {
          text: '⚠️ 确认关闭电脑吗?',
          confirm_button: '强制关机',
          cancel_button: '取消'
        } : null
      ]]]

状态可视化

yaml

state:
  - value: "on"    # 运行中
    icon: mdi:server
    color: "#00FF00"
    styles:
      icon: [animation: pulse 2s infinite]  # 呼吸灯效果
      
  - value: "off"   # 已关闭
    icon: mdi:server-off
    color: "#FF0000"
    styles:
      card: [filter: grayscale(50%)]  # 灰度显示
      
  - value: unavailable  # 状态未知
    icon: mdi:alert-circle-outline
    color: orange
    label: 状态未知

💻 虚拟机卡片(以 Windows11 为例)

yaml

- type: custom:button-card
  entity: binary_sensor.qemu_windows11_100_status
  name: Windows11
  variables:
    vm_node: sensor.qemu_windows11_100_node  # 显示所在节点
  
  tap_action:
    action: call-service
    service: button.press  # 统一使用按钮服务
    service_data:
      entity_id: |-
        [[[
          // 根据状态选择开关机按钮
          entity.state === 'off'
            ? 'button.qemu_windows11_100_start'     # 开机按钮
            : 'button.qemu_windows11_100_shutdown'  # 关机按钮
        ]]]
    confirmation: |-
      [[[ 
        // 关机确认提示
        entity.state === 'on' ? {
          text: '⚠️ 确认关闭 Windows11 虚拟机吗?',
          confirm_button: '强制关机',
          cancel_button: '取消'
        } : null
      ]]]
  
  show_label: true
  label: |  # 显示所在节点名称
    [[[ return variables.vm_node.state ]]]

🎨 统一视觉样式

yaml

styles:
  card:
    - border-radius: 15px    # 圆角设计
    - height: 115px          # 统一高度
  name:                     # 名称样式
    - font-size: 15px
    - font-weight: bold
    - font-family: 黑体      # 中文字体支持
  icon:                     # 图标位置
    - position: relative
    - top: "-10%"
  state:                    # 状态文字
    - font-size: 12px
    - color: var(--secondary-text-color)

⚙️ 技术亮点

  1. 智能服务路由

    • 根据当前状态自动选择开机/关机服务

    • PVE 主机使用不同服务类型(button/switch),虚拟机统一使用 button

  2. 动态确认机制

    • 仅在执行关机操作时弹出确认对话框

    • 开机操作直接执行无确认

  3. 状态可视化

    • 运行状态:绿色呼吸灯效果

    • 关闭状态:50% 灰度化处理

    • 未知状态:橙色警告图标

  4. 节点信息展示

    • 在卡片底部显示虚拟机所在 PVE 节点名称

    • 通过 variables 动态获取节点传感器数据


💡 使用建议

  1. 实体匹配

    yaml

    # 确保实体ID匹配您的Proxmox集成配置
    entity: binary_sensor.[your_vm_id]_status
  2. 安全增强

    yaml

    # 在 configuration.yaml 中限制服务权限
    homeassistant:
      whitelist_external_dirs:
        - /path/to/scripts
  3. 样式自定义

    • 修改 styles 中的颜色代码调整主题色

    • 调整 top/left 值微调元素位置


此配置通过高度动态化的设计,实现了 PVE 环境的优雅监控和控制,适合需要集中管理虚拟化环境的 Home Assistant 用户。

最后附上我的完整配置。

yaml

type: horizontal-stack
cards:
  - type: custom:button-card
    entity: binary_sensor.node_home_status
    name: PVE
    tap_action:
      action: call-service
      service: |-
        [[[
          // 统一服务调用逻辑
          if (entity.state === 'on') {
            return 'button.press';  // 关机操作调用按钮的 press 服务
          } else {
            return 'switch.turn_on';  // 开机操作调用 switch 的 turn_on 服务
          }
        ]]]
      service_data:
        entity_id: |-
          [[[
            if (entity.state === 'on') {
              return 'button.node_home_shutdown';  // 关机按钮实体
            } else {
              return 'switch.pve_switch';  // 开机开关实体
            }
          ]]]
      confirmation: |-
        [[[
          // 动态显示确认弹窗(仅在关机时触发)
          if (entity.state === 'on') {
            return {
              text: '⚠️ 确认关闭电脑吗?',
              confirm_button: '强制关机',
              cancel_button: '取消'
            };
          } else {
            return null;  // 开机无需确认
          }
        ]]]
    state:
      - value: "on"
        icon: mdi:server
        color: "#00FF00"
        styles:
          card: null
          icon:
            - animation: pulse 2s infinite
      - value: "off"
        icon: mdi:server-off
        color: "#FF0000"
        styles:
          card:
            - filter: grayscale(50%)
      - value: unavailable
        icon: mdi:alert-circle-outline
        color: orange
        label: 状态未知
    styles:
      custom_fields:
        graph:
          - position: absolute
          - left: "-1px"
          - top: 70.5%
        unit:
          - position: absolute
          - left: 50%
          - top: 72%
      card:
        - border-radius: 15px
        - height: 115px
      entity_picture:
        - width: 45%
      name:
        - font-size: 15px
        - align-self: middle
        - font-weight: bold
        - font-family: \9ED1\4F53
        - position: relative
        - left: 0px
        - top: "-50%"
      icon:
        - position: relative
        - left: 0px
        - top: "-10%"
      state:
        - font-size: 12px
        - font-weight: bold
        - position: relative
        - background-color: none
        - left: "-7%"
        - top: "-79%"
        - color: var(--secondary-text-color)
  - type: custom:button-card
    entity: binary_sensor.qemu_windows11_100_status
    name: Windows11
    variables:
      vm_node: sensor.qemu_windows11_100_node
    tap_action:
      action: call-service
      service: |-
        [[[
          // 状态为关闭时触发开机,否则触发关机(按钮实体已绑定对应服务)
          return 'button.press';  // 统一调用按钮的 press 服务
        ]]]
      service_data:
        entity_id: |-
          [[[
            if (entity.state === 'off') 
              return 'button.qemu_windows11_100_start';  // 开机按钮
            else 
              return 'button.qemu_windows11_100_shutdown';  // 关机按钮
          ]]]
      confirmation: |-
        [[[
          // 仅在触发关机时显示确认弹窗
          if (entity.state === 'on') {
            return {
              text: '⚠️ 确认关闭 Windows11 虚拟机吗?',
              confirm_button: '强制关机',
              cancel_button: '取消'
            };
          } else {
            return null;  // 开机无需确认
          }
        ]]]
    show_label: true
    label: |
      [[[ 
        return variables.vm_node.state; 
      ]]]
    state:
      - value: "on"
        icon: mdi:server
        color: "#00FF00"
        styles:
          card: null
          icon:
            - animation: pulse 2s infinite
      - value: "off"
        icon: mdi:server-off
        color: "#FF0000"
        styles:
          card:
            - filter: grayscale(50%)
      - value: unavailable
        icon: mdi:alert-circle-outline
        color: orange
        label: 状态未知
    styles:
      custom_fields:
        graph:
          - position: absolute
          - left: "-1px"
          - top: 70.5%
        unit:
          - position: absolute
          - left: 50%
          - top: 72%
      card:
        - border-radius: 15px
        - height: 115px
      entity_picture:
        - width: 45%
      name:
        - font-size: 15px
        - align-self: middle
        - font-weight: bold
        - font-family: \9ED1\4F53
        - position: relative
        - left: 0px
        - top: "-50%"
      icon:
        - position: relative
        - left: 0px
        - top: "-10%"
      state:
        - font-size: 12px
        - font-weight: bold
        - position: relative
        - background-color: none
        - left: "-7%"
        - top: "-79%"
        - color: var(--secondary-text-color)
  - type: custom:button-card
    entity: binary_sensor.qemu_ds918_101_status
    name: DS918
    variables:
      vm_node: sensor.qemu_ds918_101_node
    tap_action:
      action: call-service
      service: |-
        [[[
          // 状态为关闭时触发开机,否则触发关机(按钮实体已绑定对应服务)
          return 'button.press';  // 统一调用按钮的 press 服务
        ]]]
      service_data:
        entity_id: |-
          [[[
            if (entity.state === 'off') 
              return 'button.qemu_ds918_101_start';  // 开机按钮
            else 
              return 'button.qemu_ds918_101_shutdown';  // 关机按钮
          ]]]
      confirmation: |-
        [[[
          // 仅在触发关机时显示确认弹窗
          if (entity.state === 'on') {
            return {
              text: '⚠️ 确认关闭 DS918 虚拟机吗?',
              confirm_button: '强制关机',
              cancel_button: '取消'
            };
          } else {
            return null;  // 开机无需确认
          }
        ]]]
    show_label: true
    label: |
      [[[ 
        return variables.vm_node.state; 
      ]]]
    state:
      - value: "on"
        icon: mdi:server
        color: "#00FF00"
        styles:
          card: null
          icon:
            - animation: pulse 2s infinite
      - value: "off"
        icon: mdi:server-off
        color: "#FF0000"
        styles:
          card:
            - filter: grayscale(50%)
      - value: unavailable
        icon: mdi:alert-circle-outline
        color: orange
        label: 状态未知
    styles:
      custom_fields:
        graph:
          - position: absolute
          - left: "-1px"
          - top: 70.5%
        unit:
          - position: absolute
          - left: 50%
          - top: 72%
      card:
        - border-radius: 15px
        - height: 115px
      entity_picture:
        - width: 45%
      name:
        - font-size: 15px
        - align-self: middle
        - font-weight: bold
        - font-family: \9ED1\4F53
        - position: relative
        - left: 0px
        - top: "-50%"
      icon:
        - position: relative
        - left: 0px
        - top: "-10%"
      state:
        - font-size: 12px
        - font-weight: bold
        - position: relative
        - background-color: none
        - left: "-7%"
        - top: "-79%"
        - color: var(--secondary-text-color)
  - type: custom:button-card
    entity: binary_sensor.qemu_ubuntu24_103_status
    name: Ubuntu24
    variables:
      vm_node: sensor.qemu_ubuntu24_103_node
    tap_action:
      action: call-service
      service: |-
        [[[
          // 状态为关闭时触发开机,否则触发关机(按钮实体已绑定对应服务)
          return 'button.press';  // 统一调用按钮的 press 服务
        ]]]
      service_data:
        entity_id: |-
          [[[
            if (entity.state === 'off') 
              return 'button.qemu_ubuntu24_103_start';  // 开机按钮
            else 
              return 'button.qemu_ubuntu24_103_shutdown';  // 关机按钮
          ]]]
      confirmation: |-
        [[[
          // 仅在触发关机时显示确认弹窗
          if (entity.state === 'on') {
            return {
              text: '⚠️ 确认关闭 JumpServer 虚拟机吗?',
              confirm_button: '强制关机',
              cancel_button: '取消'
            };
          } else {
            return null;  // 开机无需确认
          }
        ]]]
    show_label: true
    label: |
      [[[ 
        return variables.vm_node.state; 
      ]]]
    state:
      - value: "on"
        icon: mdi:server
        color: "#00FF00"
        styles:
          card: null
          icon:
            - animation: pulse 2s infinite
      - value: "off"
        icon: mdi:server-off
        color: "#FF0000"
        styles:
          card:
            - filter: grayscale(50%)
      - value: unavailable
        icon: mdi:alert-circle-outline
        color: orange
        label: 状态未知
    styles:
      custom_fields:
        graph:
          - position: absolute
          - left: "-1px"
          - top: 70.5%
        unit:
          - position: absolute
          - left: 50%
          - top: 72%
      card:
        - border-radius: 15px
        - height: 115px
      entity_picture:
        - width: 45%
      name:
        - font-size: 15px
        - align-self: middle
        - font-weight: bold
        - font-family: \9ED1\4F53
        - position: relative
        - left: 0px
        - top: "-50%"
      icon:
        - position: relative
        - left: 0px
        - top: "-10%"
      state:
        - font-size: 12px
        - font-weight: bold
        - position: relative
        - background-color: none
        - left: "-7%"
        - top: "-79%"
        - color: var(--secondary-text-color)