# HomeAssistant，打造智能家居

## 概述

现在，大家家中或多或少都会有一些有「智能家居」功能的电器，这些电器能够连网，并且配置好之后，可以使用专用的 App 远程控制。

>[!example]  示例
>比如，在回家的路上，大约还有 10 多分钟到家，就可以远程控制家中的空调，提前打开。

当这些各式各样的电器多了以后，难免会遇到一些问题。主要的困扰在于，需要安装各式各样的 App ，这一过程十分繁杂。为了解决这一困扰，市场上出现了一些智能家居产品的集成商，比较知名的就是米家、天猫精灵和小度，只要购买与他们自家生产或者有合作关系的第一方产品，或者适配了他们平台的第三方产品，就可以使用他们提供的 App ，统一适配各种产品。

但是，这需要我们在购买之初就做好一些功课，选择可以适配的产品，这样会减少我们可以选择的余地。并且，可能我们已经购买了多个品牌的产品，并且近期并不需要更换，所以还是被迫要安装很多的 App。或者，也可能像本人这样，房子是租的，能用什么样的智能家居产品，取决于房东的选择。

那么，如果我们手头上已经有了一堆各式各样的产品，我们想要使用一个 App 去集中控制它们，并且我们希望各个产品之间可以形成联动，可以按照设定的逻辑自动开启、关闭和执行任务，那么配置大名鼎鼎的 [Home Assistant](https://www.home-assistant.io/) 就是一个不错的选择。

## 安装

### 安装 Home Assistant

使用 Docker 进行安装，方法如下：

```dockerfile
docker run -itd --name HomeAssistant --restart=always \
--privileged \
-e TZ=Asia/Shanghai \
-v /「PATH」:/config \
-p 8123:8123 \
ghcr.io/home-assistant/home-assistant:latest
```

>[!info]  信息
>官网是推荐使用 Host 模式的，这种方式安装使用会比较顺利。我这里使用的桥接，感觉影响不大。

### 安装 HACS

[HACS](https://hacs.xyz/) 全称是 Home Assistant Community Store，也就是 Home Assistant 的社区商店，安装之后安装其他插件就会轻松许多。

安装方式也很简单，首先进入 Home Assistant 的容器中：

```bash
docker exec -it xxxxx sh
```

然后执行下面这行命令：

```bash
wget -O - https://get.hacs.xyz | bash -
```

命令执行完毕，就会自动将文件下载并解压到对应位置。

### 启用 HACS

首先，先重启一下容器，会重新加载一遍新增的内容。

点击`设置->设备与服务->添加集成->输入HACS->点击结果`

![](https://img.papergate.top:5000/i/2025/08/689238e424fd3.webp)


进行相应的配置后就启用了 HACS

## 看板配置

简单来说，我们使用 Home Assistant 所要实现的效果，就是制作一定数量的看板，并且按照一定的排版展示出来。

这些看板有一些用来展示家中各类电器的状态，一些用来控制家中各类电器的启停。比如下面这个就是一个看板，用来展示家中 Nas 设备的使用详情信息。

![](https://img.papergate.top:5000/i/2025/08/68923ad5c345b.webp)

### 配置文件目录

为了讲清楚看板如何制作，先讲一下 Home Assistant 配置文件的目录详情：

我们使用 Docker 安装了 Home Assistant，并且将`/config`目录映射到了外部。Home Assistant 的主要配置文件都在这个`/config`目录下，有「三个目录」和「一个文件」比较重要：

- `custom_components`：集成组件，之前安装的 HACS 就在其中，可以理解成是「后端」，用来向前端页面提供数据，执行命令。起到适配器作用，负责和电器通信，控制电器。
- `themes`：主题，可以理解成是「前端样式」，配置好以后，看板的风格就确定下来了。
- `www`：卡片，可以理解成是「前端结构」，配置好以后，看板的布局就确定下来了。
- `configuration.yaml`：主要的配置文件。

### 常见组件

配置集成组件后就可以连接到对应的电器了，在 HACS 中下载：

- `Xiaomi Home`：连接支持米家的设备，可以把米家中的设备导入到 Home Assistant 中。
- `Midea AC Lan`：连接美的设备。
- `天气预报`：可以获取最近一段时间某个地区的天气情况。

### 常见卡片

在 HACS 中搜索对应卡片下载即可：

- `Mushroom`：内置了很多美观的卡片，基本上是人手必备的卡片。
- `apexcharts-card`：绘图卡片，将数据展示为图表。
- `mini-graph-card`：另外一个绘图卡片，将数据展示为图表。
- `Tabbed Card`：很方便生成 Tab 页，一块空间中可以很方便堆放多个相似的卡片。
- `Colorfulclouds Weather Card`：彩云卡片，专门用来展示天气情况的，和天气预报配合使用。

### 主题

我用的这款主题叫做 `visionOS Theme`，在 HACS 中搜索对应卡片下载即可。

### 仪表盘

#### 新增仪表盘

点击`设置->仪表板->添加仪表板->从新建仪表盘开始`

其他几个选项用到的不多，默认选择新建仪表盘就可以了。

#### 创建视图

一个仪表盘可以创建多个视图，如下：

![](https://img.papergate.top:5000/i/2025/08/68924581bba31.webp)

点击「箭头」可以左右移动视图的位置，点击「加号」新增视图，「铅笔」图标可以编辑视图。

#### 编辑视图

点击「编辑视图」，如下：

![](https://img.papergate.top:5000/i/2025/08/689245f71e01d.webp)

布局可以选择四种，部件、瀑布流、侧边栏、面板，选择前两种就可以。

- 如果需要显示的内容比较多，需要分列显示，就选择「部件」
- 如果需要显示的内容较少，一列就可以了，就选择「瀑布流」

其他的配置很多都可以使用默认的值，稍微修改并对比一下，很快就能理解。

#### 新增卡片

- 「部件」布局的时候，点击页面上的「加号」来新增卡片
- 「瀑布流」布局的时候，点击右下角的「添加卡片」来新增卡片

大部分的卡片可以通过图形化的界面进行配置，卡片的配置过程就是将后端数据和前端模板连接到过程。配好以后，卡片中原本设定要展示数据的区域就会展示你指定的数据。

比如这个空调卡片：

![](https://img.papergate.top:5000/i/2025/08/68924929a3cc1.webp)

在本人指定好「实体」（组件连接到电器以后，就会生成一个或者多个实体）以后，就基本配置完成了。

点击左下角的「显示代码编辑器」，可以看到如下的`yaml`格式的文字：

```yaml
type: thermostat
entity: climate.xxxxx
name: 卧室空调
features:
  - type: climate-hvac-modes
```

有一些复杂一些的界面，只能使用`yaml`进行配置，不提供图形化界面。

比如之前展示的 Nas 详情信息：

![](https://img.papergate.top:5000/i/2025/08/68923ad5c345b.webp)

```yaml
type: custom:tabbed-card
styles:
  "--mdc-theme-primary": white
  "--mdc-tab-text-label-color-default": orange
options: null
tabs:
  - card:
      type: custom:apexcharts-card
      chart_type: radialBar
      header:
        show: true
        show_states: true
        colorize_states: true
      all_series_config:
        min: 0
        max: 100
      series:
        - entity: sensor.cpu_usage
          name: CPU 使用率
        - entity: sensor.memory_percent
          name: 内存使用率
        - entity: sensor.disk_percent
          name: 硬盘使用率
    attributes:
      label: 使用详情
  - card:
      type: custom:apexcharts-card
      chart_type: donut
      header:
        show: true
        show_states: true
        colorize_states: true
      series:
        - entity: sensor.memory_used
          name: 已用内存
          unit: GB
        - entity: sensor.memory_available
          name: 可用内存
          unit: GB
    attributes:
      label: 内存详情
  - card:
      type: custom:apexcharts-card
      chart_type: donut
      header:
        show: true
        show_states: true
        colorize_states: true
      series:
        - entity: sensor.disk_used
          name: 已用存储
          unit: TB
        - entity: sensor.disk_available
          name: 可用存储
          unit: TB
    attributes:
      label: 硬盘详情

```

## 自定义实体

一般来说，组件连接到电器，就能够生成符合我们要求的实体。但是，如果我们想要在卡片上展示一部分我们自己获取到数据，就需要自己去编写相应的 API。

我使用的方法非常的简单，并且可以绕过 Home Assistant 自身的一些生态，非常直接的实现想要的效果。

### `rest` 传感器

Home Assistant 提供了名为 Rest 的传感器，这种传感器的数据通过定期调用 Restful API 来获取。

在`configuration.yml`中添加如下内容：

```yaml
sensor: !include configuration/sensor.yaml
```

然后创建`configuration/sensor.yaml`文件，写入如下内容：

```yaml
- platform: rest
  name: nas_info
  resource: "http://192.168.0.103:8000/nas_info/"
  scan_interval: 60
  json_attributes:
    - cpu
    - memory
    - disk
```

然后使用`fastapi`写一个 API 如下：

```python
from fastapi import APIRouter  
import psutil  
from typing import Dict, Any  
  
router = APIRouter(prefix="/nas_info", tags=["nas_info"], responses={})  
  
@router.get("/", response_model=Dict[str, Any])  
async def nas_info():  
    cpu_info = {"percent": psutil.cpu_percent(interval=1)}  
    mem = psutil.virtual_memory()  
    memory_info = {"percent": mem.percent, "available": f"{mem.available / (1024 ** 3):.2f}",  
                   "used": f"{mem.used / (1024 ** 3):.2f}"}  
    usage = psutil.disk_usage('/')  
    disk_info = {"percent": usage.percent, "used": f"{usage.used / (1024 ** 4):.2f}",  
                 "available": f"{usage.free / (1024 ** 4):.2f}"}  
    return {"cpu": cpu_info, "memory": memory_info, "disk": disk_info}
```

这样，每隔`60s`，传感器`sensor.nas_info`就会调用一次 API，获取到返回的`json`数据。

### `template` 模板

template 模板负责进行数据的格式转换，可以拆分复杂的数据格式，在`configuration.yml`中添加如下内容：

```yaml
template: !include configuration/template.yaml
```

然后创建`configuration/template.yaml`文件，写入如下内容：

```yaml
sensors:
  cpu_usage:
    friendly_name: "BlackHole CPU 使用率"
    unit_of_measurement: "%"
    value_template: "{{ state_attr('sensor.nas_info', 'cpu').percent}}"
  memory_percent:
    friendly_name: "BlackHole 内存使用率"
    unit_of_measurement: "%"
    value_template: "{{ state_attr('sensor.nas_info', 'memory').percent}}"
  memory_used:
    friendly_name: "BlackHole 已用内存"
    value_template: "{{ state_attr('sensor.nas_info', 'memory').used }}"
  memory_available:
    friendly_name: "BlackHole 可用内存"
    value_template: "{{ state_attr('sensor.nas_info', 'memory').available }}"
```

因为 Home Assistant 也是用 Python 写的，所以里头的模板语言用的是`jinja`

>[!info]  信息
>Home Assistant 的大部分卡片，只支持特定数据类型的实体，比如要求实体是数值型的、要求是百分比、要求是空调、扫地机器人等电器，通常`json`格式的数据无法直接放到卡片中，所以要用 template 模板转换一下。

### 实体信息获取

有时候，我们需要获取实体的详细信息。比如，我们需要从空调实体中获取当前房间的温湿度信息。可以采用如下方式：

#### `api/ha_entity.py`

```python
from fastapi import APIRouter  
from typing import Dict, Any  
from core.homeassistant import get_entity  
  
router = APIRouter(prefix="/ha_entity", tags=["ha_entity"], responses={})  
  
  
@router.get("/{entity_id:str}", response_model=Dict[str, Any])  
async def air_conditioner_info(entity_id: str):  
    info = get_entity(entity_id)  
    return info
```

#### `core/homeassistant.py`

```python
import requests  
  
home_assistant_url = "http://HOST:8123"  
api_token = "xxxxx"  
  
  
# 获取实体状态  
def get_entity(entity_id):  
    url = f"{home_assistant_url}/api/states/{entity_id}"  
    headers = {"Authorization": f"Bearer {api_token}", "Content-Type": "application/json"}  
    response = requests.get(url, headers=headers)  
  
    if response.status_code == 200:  
        data = response.json()  
        return data
```

>[!info]  信息
>官方有一个库，叫做`homeassistant_api`，可以使用`pip`进行安装，可以更简单实现这个效果。本人这里使用 HTTP 请求进行获取是因为本人的一个设备返回的`json`数据太长被截断了，使用官方的库会报错。

使用这个 API 访问一下本人的空调，得到一个类似如下的`json`数据：

```json
{
  "entity_id": "climate.xxxxx",
  "state": "cool",
  "attributes": {
    "current_temperature": 24.6,
    "temperature": 26,
    "current_humidity": 55,
    "fan_mode": "auto",
    "preset_mode": "none",
    "swing_mode": "off"
    }
}
```

于是就可以获取到当前房间的温度，创建一个 template 实体：

```yaml
current_temperature:
        friendly_name: "卧室室内温度"
        value_template: "{{ state_attr('climate.xxxxx', 'current_temperature') }}"
```

其他设备的信息获取可以参照这个进行。

### `rest_command` 动作

Home Assistant 有一个叫做「自动化」的功能。比如我离开家的时候，我想把空调关了，比如我快到家的时候，我想把空调打开。

自动化需要触发条件和动作两项。触发条件比较好设置，而动作通常会比较复杂，如果使用自带的图形化界面会比较繁琐，可以创建一个 `rest_command` 动作，触发条件以后，调用 API 执行一系列的动作。

在`configuration.yaml`中进行如下配置：

```yaml
rest_command: !include configuration/rest_command.yaml
```

然后创建`configuration/rest_command.yaml`文件，写入如下内容：

```yaml
leave_home:
  url: "http://192.168.0.103:8000/leave/"
  method: POST
  payload: '{"place": "home"}'
  headers:
    Content-Type: "application/json"
```

这样，触发离家这个条件的时候，就会使用 POST 请求调用`http://192.168.0.103:8000/leave/`，并且传入参数`{"place": "home"}`

这样，只要我们写一个 `API`来处理后续事件即可。

>[!info]  信息
> 使用 `rest_command` 动作还有一个好处，就是可以在中途调用`n8n`和`mcp`服务，实现更加复杂的控制效果，比如调用`高德地图`的`mcp`，推算出到家的时间。

---

> 作者: Aphros  
> URL: https://blog.papergate.top/posts/homeassistant%E6%89%93%E9%80%A0%E6%99%BA%E8%83%BD%E5%AE%B6%E5%B1%85/  

