# Matter

> Matter 驱动参考：设备加入的 Wi-Fi 和 Thread 网络、通过蓝牙把设备调试进本地 Fabric、配对设备的流程、节点身份，以及来自设备的点位和写入每个点位会做什么。

Matter 驱动读的是 **Matter** 仪器：通过 Matter 标准认证的智能传感器、插座、灯具之类的设备，不管它们是通过 **Wi-Fi** 还是通过 **Thread** 加入网络。两种都由同一个驱动负责。网络只是进门的路：设备一旦进了网络，Ganter Lab 就用同样的方式、通过局域网去读它。

Ganter Lab 扮演的是一个**带着自己本地 Fabric 的 Matter 控制器**：它在本机上通过**蓝牙低功耗**把每一台设备调试入网一次，把登记在驱动**网络**卡片上的某个网络的凭据交给它，从那以后就通过局域网和它对话：Wi-Fi 设备经过你的路由器，Thread 设备经过你的 Thread 边界路由器。

你从来不用告诉 Ganter Lab 一台设备是 Wi-Fi 还是 Thread：**设备自己会说**。蓝牙会话一打开，Ganter Lab 在往设备上写任何东西之前，先从设备读出它加入哪一种网络；如果选中的网络是另一种，它就停下来，并给出一条专门说明这件事的消息。

这把通常的配置顺序颠倒了过来。Matter 设备不是从一个主机和端口开始的：它是从一次**配对仪式**开始的，事后存下来的只有那个已调试节点稳定的身份。

配对之前你需要：

- 本机上一块**蓝牙低功耗适配器**，处于打开状态，并且 Windows 允许 Ganter Lab 使用它。
- 设备要加入的网络，已经登记在**网络**卡片上：Wi-Fi 设备需要 Wi-Fi 名称和它的密码；Thread 设备需要一个已有的、带边界路由器的 Thread 网络，以及它完整的**活动运行数据集**。Ganter Lab 把设备加入你的网络；它编不出这个网络的凭据。
- 设备的**配网码**，印在设备上或者它的包装上，并且设备处于配对模式。

## 网络 {#networks}

Matter 驱动的面板上有一张标题为**网络**的卡片：设备要加入的 Wi-Fi 和 Thread 网络，一行一个。一个网络在**第一台设备之前登记一次**，之后配对进这个网络的每一台设备都用它，所以在同一个 Wi-Fi 上配对第二盏灯时，什么都不用再填。这张卡片只对可以配置 Connector 的角色绘制，因为它保存着网络的秘密，而且除了配对别无用处。

卡片的头部按这个顺序放着它的命令：

| 命令 | 做什么 | 不做什么 |
|---|---|---|
| **添加 Wi-Fi 网络** | 添加一个 Wi-Fi 网络并选中它。本机连着的 Wi-Fi 的名称还没登记过时，新网络就用这个名称；否则它先不带名称，由你来填写。Windows 只交出名称，而且只在它允许应用知道本机位置的时候才交出（Windows 11 会就此请求权限）；密码永远由你自己输入。 | 它不从 Windows 读取密码，也不检查这个网络是否存在。 |
| **添加 Thread 网络** | 添加一个 Thread 网络并选中它；在你给它起名之前，它用一个临时名称（已被占用时加上编号），并等着你填上数据集。 | 它不和边界路由器对话：Ganter Lab 只使用边界路由器所建网络的数据集。 |
| **移除网络** | 移除选中的网络，之前会弹出一次确认，点出这个网络的名字，并说明已经配对的设备不受影响。没有选中任何网络时，它仍然画着、呈灰色，它的提示会说明原因。 | 它不碰已经配对进这个网络的设备：这些设备照常工作，并在自己的**网络**字段里保留这个名称。 |

表格列出每个网络的**名称**、**网络类型**（**Wi-Fi 网络**或**Thread 网络**）和**状态**：**可以配对**，或者这个网络还缺什么。还没写名称的网络显示为**未命名的网络**。

选中网络的字段就在表格下方原地编辑，并且自动保存。下面的规则拒绝的值不会写入，字段会说明原因。

| 类型 | 字段 | 它是什么 | 格式 |
|---|---|---|---|
| Wi-Fi | **Wi-Fi 名称** | 交给设备的名称（SSID），必须和路由器广播的一模一样：区分大小写。每个 Wi-Fi 名称只登记一次。 | 1 到 32 字节的文本（UTF-8）。 |
| Wi-Fi | **密码** | 交给设备的 Wi-Fi 密码。 | 开放网络留空；否则是 8 到 63 个可打印的 ASCII 字符（WPA 口令），或者正好 64 位十六进制数（原始密钥）。 |
| Thread | **名称** | 你给这个网络起的名字，用来在配对时和每台设备上认出它。每个 Thread 网络名称只登记一次。 | 自由文本。 |
| Thread | **Thread 操作数据集** | 你那个 Thread 网络完整的活动数据集，来自网络管理员或者它的边界路由器。Home Assistant 会显示它；Alexa 和 Apple 不会。 | 十六进制文本（空白字符和一个 `hex:` 前缀都能容忍），解码之后最多 254 字节。它必须是完整的数据集：Active Timestamp、Channel、Channel Mask、Extended PAN ID、Mesh-Local Prefix、Network Key、Network Name、PAN ID、PSKc 和 Security Policy 必须一个不缺，残缺或畸形的数据集会被拒绝，并点名缺的是哪一项。 |

一台 Wi-Fi 设备到底能不能配对，取决于两件事，卡片在字段旁边把两件都写着：

- **大多数设备只加入 2.4 GHz 网络。**在 2.4 GHz 和 5 GHz 上广播同一个名称的路由器可以用；只存在于 5 GHz 的网络不行。
- **本机和这些设备必须在同一个局域网里。**访客网络，或者路由器上打开的客户端隔离，会让本机在设备入网之后找不到它们。

一个网络只有在它的秘密符合这些规则时才能用于配对：Wi-Fi 网络要有合法的名称和合法的密码（空也算），Thread 网络要有合法的数据集。

编辑一个网络，改变的是**下一次**配对交出去的东西，从不改变设备已经收到的东西。需要换到另一个网络的设备，或者路由器换了新密码的设备，要复位后重新配对。

## 配对设备的流程 {#the-pair-device-flow}

在 Matter 驱动上，其他驱动提供“添加设备”的位置，这里的命令栏提供的是**配对设备**，这一栏“新建…”菜单里的 Matter 那一项也提供它。还没有任何网络能用于配对时，它仍然画着、呈灰色，提示是“请先添加设备要加入的网络。”有可用的网络时，它会打开一个表单：

| 字段 | 它是什么 | 格式 | 默认值 |
|---|---|---|---|
| 设备名称 | 这台设备将来在 Connector 里带着的名字，可编辑。 | 自由文本 | `Matter device` |
| Matter 配网码 | 那串官方的手工配对码，或者完整的 QR 载荷。输入时打码，旁边有一个显示控件。 | 一串 11 位或 21 位的手工输入码（短横线和空格会被忽略；校验位会被核对），或者以 `MT:` 开头的完整 QR 载荷。不声明支持蓝牙调试入网的 QR 载荷会被拒绝，保留的或超出范围的配网码也一样。 | 空 |
| 网络 | 设备要加入的已登记网络。只有一个可用网络时，表单用一行字说明是哪个网络、什么类型，没有什么可选的；有多个时，它是一个选择列表。 | **网络**卡片上可用网络中的一个 | 只有一个时就是它 |
| 目标文件夹 | 只在某个文件夹的“新建设备”打开表单时出现：配对的设备要归入的文件夹。用“配对设备”打开时没有这一行，设备落在驱动自己的层级。 | 只读 | 打开表单的文件夹 |

Matter 驱动的文件夹提供**新建设备**（在它的“设备”表、它的右键菜单，以及新建… > Matter > `<Folder>` 下面）。它打开同一个表单，没有可用网络时同样置灰，配对的设备归入那个文件夹。

名称和配网码都填好、并且有可用的网络之前，表单里的**配对设备**一直不可用。配对进行时，表单会报告它走到哪一步：**校验**（检查配网码和网络的秘密）、**查找**（通过蓝牙搜索设备）、**配对**（用配网码做认证，并读出设备加入哪一种网络）、**加入网络**（把网络发给设备，然后在局域网上反复询问它，直到它应答；状态行会说明这个网络是 Wi-Fi 还是 Thread）、**保存**（和局域网上的设备建立安全会话，完成它的调试入网，并把节点记进本地 Fabric）。

在物理设备还没被改动之前，**取消**是可用的；一旦调试入网已经改动了设备，这个按钮就写着 Ganter Lab 正在安全收尾，流程会把这条记录做完，而不是丢下一个调试到一半的节点。一次只跑一次配对；一次配对进行期间的第二次尝试会被拒绝。

新设备会记下它加入的网络的名称，显示在它的**网络**字段里。在同一个网络上配对下一台设备时，只需要填它的名称和配网码。

## 配对失败时 {#when-pairing-fails}

配对失败时，消息会说明原因和该怎么做：

| 原因 | 含义 | 仪器 | 怎么做 |
|---|---|---|---|
| 配网码无效 | 这串码既不是官方的 11 位或 21 位手工输入码，也不是完整的 `MT:` QR 载荷。 | 没被动过 | 把印着的码再看一遍。 |
| Thread 数据集无效 | 选中的 Thread 网络的数据集不完整或格式不对。 | 没被动过 | 在**网络**卡片上，把完整的活动数据集重新复制进这个网络。 |
| Wi-Fi 名称或密码无效 | 选中的 Wi-Fi 网络的名称或密码违反了**网络**卡片的规则。 | 没被动过 | 在卡片上改正。 |
| 蓝牙缺失、被关掉或被拒绝 | 本机没有可用的蓝牙低功耗适配器、蓝牙被关掉了，或者 Windows 拒绝了 Ganter Lab 访问它。 | 没被动过 | 打开蓝牙，或者在 Windows 里允许 Ganter Lab 使用它，然后再试一次。 |
| 没有找到匹配的设备 | 没有处于配对模式的设备回应这个配网码。 | 没被动过 | 让设备进入配对模式，放在本机附近，再试一次。 |
| 配网码被驳回 | 设备驳回了这个码。 | 没被动过 | 把印着的码再看一遍，并让设备重新进入配对模式。 |
| 设备通过 Wi-Fi 加入 | 选中的网络是 Thread。 | 没被动过：只读了它，什么都没写进去 | 登记设备要加入的 Wi-Fi 网络，选中它重新配对。 |
| 设备通过 Thread 加入 | 选中的网络是 Wi-Fi。 | 没被动过：只读了它，什么都没写进去 | 登记这个 Thread 网络，选中它重新配对。 |
| 设备是有线的（以太网） | 通过蓝牙配对不适用于有线设备。 | 没被动过：只读了它，什么都没写进去 | Ganter Lab 无法配对这台设备。 |
| 设备不说自己的网络 | 设备既没报 Wi-Fi，也没报 Thread 或以太网。 | 没被动过：只读了它，什么都没写进去 | Ganter Lab 无法配对这台设备。 |
| 找不到 Wi-Fi 网络 | 设备回答说找不到叫这个名字的 Wi-Fi：拼写或大小写写错了、网络只在 5 GHz 上，或者设备超出了信号范围。 | 什么都不保留：它丢掉收到的东西，回到配对模式。没有节点 ID。 | 在**网络**卡片上改正名称，确认路由器在 2.4 GHz 上广播它，然后重新配对。 |
| Wi-Fi 密码被拒绝 | 设备回答说 Wi-Fi 拒绝了这个密码。 | 什么都不保留：它丢掉收到的东西，回到配对模式。没有节点 ID。 | 在**网络**卡片上改正密码，然后重新配对。 |
| 设备无法加入网络 | 设备回答说它加入不了网络，原因不是上面两种（Thread 的话，任何拒绝都算，包括找不到网络）。 | 什么都不保留：它丢掉收到的东西，回到配对模式。没有节点 ID。 | 在**网络**卡片上检查这个网络（Thread 的话，重新复制当前的活动数据集，并确认边界路由器开着），然后重新配对。 |
| 设备没有出现在网络上 | 设备对网络没有给出任何回答，或者之后始终没有出现在局域网上。有些设备要离开蓝牙才能入网，根本没法回答。 | 已调试入网：消息会显示节点 ID | Wi-Fi 的话，确认本机和设备在同一个局域网里，中间没有访客网络或客户端隔离；Thread 的话，确认边界路由器开着并且连得上。然后用设备发现把节点找回来。 |
| 安全会话没有建立 | 设备已加入网络，也在网络上找到了，但与它的安全会话，或通过该会话完成配对的步骤失败了。诊断日志会记下停在哪一步以及设备的回答。 | 已调试入网：消息会显示节点 ID | 用设备发现把节点找回来。如果设备开着，设备发现却找到它而没有任何值，就让设备重新进入配对模式，然后重新配对。 |
| 另一次配对正在进行 | 一次只跑一次配对。 | 没被动过 | 等它结束后再试一次。 |
| 本地 Fabric 无法保存 | Ganter Lab 没能安全地保存 Fabric。 | 发生在配对开始之前时没被动过；发生在之后时已调试入网，并附带节点 ID | 先解决存储问题，再复位任何东西。 |

只要失败发生在仪器已经被调试入网之后，消息就会说出来并显示节点 ID：这个节点**留在 Fabric 里**，等它上了网，日后可以用“发现设备”把它加进来。这种情况下不要复位设备；只有一条说“什么都没有保存”的消息，才表示仪器完全没被动过。

回答说自己加入不了网络的设备是例外：它身上没有值得留下的东西。Ganter Lab 在设备上结束这次配对，设备丢掉收到的东西并立刻回到配对模式，同时把它的节点从 Fabric 里移除；所以消息里没有节点 ID，“发现设备”也永远不会列出一个无法应答的节点。改正网络，然后重新配对。只有完全没有回答的设备才保留节点，因为它有没有入网，要等它出现在局域网上才知道。

## 保存了什么 {#what-is-stored}

| 什么 | 存在哪里 | 怎么存 |
|---|---|---|
| 网络的名称和类型 | 工作站的配置 | 按写下的样子。 |
| Wi-Fi 密码和 Thread 操作数据集 | 工作站的配置 | 和设备密码一样，为输入它们的那个 Windows 用户加密保护：换一个 Windows 账户，它们读出来是空的，必须重新输入。备份不带上它们；[导出到另一台计算机](settings-backups#export-for-another-machine)会带上它们，用导出自己的密码封好。 |
| 配网码 | 哪里都不存 | 它只在配对进行时存在于表单里，配对结束、失败或被取消时就被清掉。 |
| 设备加入的网络 | 设备的配置 | 它的名称，显示在设备的**网络**字段里。 |
| 设备的身份 | 设备的配置和本地 Fabric | 节点 ID。 |

Wi-Fi 密码和数据集从不出现在诊断日志里、设备的地址里，也不出现在它的 Thing Description 里。网络和配对不通过 MCP 服务器提供给智能体：配对需要有人在设备旁边，而网络保存着秘密。

## 节点身份 {#node-identity}

配对好的设备绑定在仪器那个稳定的 64 位**节点 ID** 上，它在设备的“连接”卡片里以 16 位十六进制数只读显示，旁边就是设备状态。可编辑的显示名称归你改；节点 ID 不归。存下来的身份已经读不出来的设备，会在那个位置显示**需要重新配对**。

设备的面板上还有一个只读的**网络**字段：设备配对时加入的那个网络的名称。由一个还不记录它的版本配对的设备，会说明没有记录任何东西。这个字段是一条记录，不是一项设置：在**网络**卡片上编辑或删除那个网络，不会改变设备上的任何东西。

因为身份就是节点 ID，所以各台 Matter 设备的连接状态彼此独立：禁用、删除或者丢掉一个节点，不会让另一个变得不可用；而删掉一台设备，只是放开它对那条安全会话的占用，并不会把仪器从本地 Fabric 里移除。

## 设备发现 {#discovery}

Matter 驱动上的**发现设备**列出已经调试进本应用本地 Fabric 的那些节点，并从每个节点读取它公开的值。为了读这些值，它要和每个节点打开一个安全会话，所以不在网络上的节点在会话打开失败之后仍然列出来，只是没有值。设备发现的候选项带的是节点身份，不带任何配网秘密。一般的设备发现流程见[设备发现](connector-discovery)。

| 节点 | 它的卡片 | 它的命令 |
|---|---|---|
| 这里没有它的设备：设备被删掉了，或者配对结束时没有留下设备 | 它公开的值，每个都已勾选 | **添加设备**创建这台设备，勾选的值成为它的点位，不必再做一次仪式。 |
| 是一台已配置的设备，有些值还没有点位 | 标为已经配置过，写明它公开的值里有几个还不是点位，并列出这些值，默认勾选 | **添加所选点位**把勾选的值添加到这台设备上；**打开**打开这台设备。 |
| 是一台已配置的设备，每个值都有点位 | “已经配置过，设备公开的每个值都已有点位。” | **打开**打开这台设备。 |
| 是一台已配置的设备，没有报告任何值 | “已经配置过。设备没有报告任何值：等设备在线后，再运行一次‘发现设备’。” | **打开**打开这台设备。 |

已配置设备的值按输出、簇和属性与它的点位比对，从不按名称：你改过名字的点位照样算数，同一个值也绝不会添加两次。

设备自己的页面上也能做到同样的事，不用离开：“点位”卡片标题栏里的**发现点位**只读这一个节点，列出它还没有点位的值，并且已勾选，旁边是**添加所选点位**（见[发现一台设备的点位](connector-discovery#discovering-a-devices-tags)）。

## 点位 {#tags}

Matter 设备的点位来自设备本身：没有人需要为它们输入簇或属性编号。配对顺利结束后会直接走到这一步。记录条写“已配对 Matter 设备‘厨房灯’。”，页面在 Matter 驱动上打开，新设备在它的“设备”表格里突出显示，并立刻只扫描这一台设备。配对创建的设备出现在**发现的设备**里，它公开的值全部勾选，扫描结束时写“勾选‘厨房灯’中要创建为点位的值，然后点‘添加所选点位’。”取消勾选你不要的值，再点**添加所选点位**；记录条随后写“已向‘厨房灯’添加 5 个点位。”还没有应答的设备什么也不报告：“‘厨房灯’还没有报告任何值。等设备在线后，再运行一次‘发现设备’。”以后每次**发现设备**，同一张卡片都会回来，带着设备还没有点位的那些值。

**添加所选点位**只为拥有“配置 Connector”权限的角色画出来。扫描或配对进行中、工作站拒绝更改时，以及什么都没勾选时（“请至少勾选一个要添加的值。”），它保持灰色，并把原因写在提示里。设备已经在读其输出、簇和属性的值绝不会再添加一次；勾选的值全都已经在了时，什么都不保存：“勾选的每个值在‘厨房灯’上都已有点位，所以没有添加任何内容。”设备上另一个点位已经用了的名称，后面会加上编号，比如亮度 (2)。

扫描按 Matter 规范的组织方式读取设备公开的内容：

| 读什么 | 设备在哪里说明 |
|---|---|
| 它的输出（endpoint） | endpoint 0 上的 Descriptor 簇（0x001D），属性 `PartsList` |
| 每个输出的簇 | 该输出上的 Descriptor，属性 `ServerList` |
| 每个簇的值 | 该簇的属性 `AttributeList`（0xFFFB） |
| 每个簇接受的命令 | 该簇的属性 `AcceptedCommandList`（0xFFF9） |
| 每个簇实现的可选功能 | 该簇的属性 `FeatureMap`（0xFFFC） |
| 每个值的当前读数 | 值本身，读一次 |

扫描提供什么，用什么名称：

| 一个值 | 提供方式 |
|---|---|
| 驱动认识的值（下一节） | 用工作站语言的清楚名称和规范给它的类型提供；只有设备接受规范改变它的方式时才可写，否则只读 |
| 驱动认识、但属于簇没有实现的功能的值：没有 Hue and Saturation 功能时的色调和饱和度，没有 Color Temperature 功能时的色温 | 不提供 |
| 驱动不认识的值 | 只读，用它在 Matter 规范里的名称（`Level Control OnLevel`），驱动也不认识这个属性时用簇和属性编号（`On/Off attribute 0x131B0001`）；类型取设备返回的读数的类型 |
| 在不止一个输出上的同一个值 | 名称写明是哪个输出，比如亮度（输出 2） |
| 全局属性（0xFFF8 到 0xFFFD），或者节点本身（endpoint 0）的值 | 不提供 |
| 列表或结构、驱动不支持的类型的值、设备没有返回读数的值 | 不列出，并在扫描收尾的那一行里计数 |

作为可写提供的点位到达时访问是读写；其余的一律只读。

## 驱动认识的值 {#the-values-the-driver-knows}

驱动带着一张取自 Matter 规范的表，列出它按名称认识的值，以及每个值怎么改变。一个值有三种改变方式之一：由驱动发送的命令改变，设备必须把这条命令列在它接受的命令里；由写入属性本身改变，前提是规范规定它可写而且设备列出了它；或者根本不改变，那是设备只报告的值。

| 值 | 簇和属性 | 类型 | 写入它会让设备做什么 | 规范规定的范围 |
|---|---|---|---|---|
| 开启 | On/Off（0x0006），`OnOff`（0x0000） | Boolean | true 发送命令 `On`（0x01），false 发送 `Off`（0x00）；只有设备两条都接受时才可写 | true 或 false |
| 亮度 | Level Control（0x0008），`CurrentLevel`（0x0000） | UInt32 | 发送 `MoveToLevelWithOnOff`（0x04），带上亮度级别、过渡时间 0，不覆盖任何选项：输出以该级别点亮，到最小值时熄灭 | 0 到 254 |
| 色调 | Color Control（0x0300），`CurrentHue`（0x0000） | UInt32 | 按最短方向发送 `MoveToHue`（0x00），过渡时间 0；仅在有 Hue and Saturation 功能时 | 0 到 254 |
| 饱和度 | Color Control（0x0300），`CurrentSaturation`（0x0001） | UInt32 | 发送 `MoveToSaturation`（0x03），过渡时间 0；仅在有 Hue and Saturation 功能时 | 0 到 254 |
| 色温 | Color Control（0x0300），`ColorTemperatureMireds`（0x0007） | UInt32 | 发送 `MoveToColorTemperature`（0x0A），过渡时间 0；仅在有 Color Temperature 功能时。设备把值保持在它自己的限值之内 | 0 到 65279，单位是迈尔德 |
| 窗帘位置 | Window Covering（0x0102），`CurrentPositionLiftPercent100ths`（0x000E） | UInt32 | 发送 `GoToLiftPercentage`（0x05） | 0（打开）到 10000（关闭），单位是百分之一个百分点 |
| 室内温度 | Thermostat（0x0201），`LocalTemperature`（0x0000） | Float（°C） | 什么也不做：只读 | -27315 到 32767，单位是百分之一摄氏度；读数为 -273.15 到 327.67 °C |
| 制冷设定值 | Thermostat（0x0201），`OccupiedCoolingSetpoint`（0x0011） | Float（°C） | 写入该属性。设备拒绝超出它自己限值的设定值 | -27315 到 32767，单位是百分之一摄氏度；读数为 -273.15 到 327.67 °C |
| 制热设定值 | Thermostat（0x0201），`OccupiedHeatingSetpoint`（0x0012） | Float（°C） | 写入该属性。设备拒绝超出它自己限值的设定值 | -27315 到 32767，单位是百分之一摄氏度；读数为 -273.15 到 327.67 °C |
| 风扇模式 | Fan Control（0x0202），`FanMode`（0x0000） | UInt32 | 写入该属性 | 0 关，1 低，2 中，3 高，4 开，5 自动，6 智能 |
| 风扇速度设定 | Fan Control（0x0202），`PercentSetting`（0x0002） | UInt32 | 写入该属性 | 0 到 100 百分比 |
| 风扇速度 | Fan Control（0x0202），`PercentCurrent`（0x0003） | UInt32 | 什么也不做：只读 | 0 到 100 百分比 |
| 温度 | Temperature Measurement（0x0402），`MeasuredValue`（0x0000） | Float（°C） | 什么也不做：只读 | -27315 到 32767，单位是百分之一摄氏度；读数为 -273.15 到 327.67 °C |
| 湿度 | Relative Humidity Measurement（0x0405），`MeasuredValue`（0x0000） | Float（%） | 什么也不做：只读 | 0 到 10000，单位是百分之一个百分点；读数为 0 到 100 % |
| 照度 | Illuminance Measurement（0x0400），`MeasuredValue`（0x0000） | UInt32 | 什么也不做：只读 | 0 到 65534，即 10000 × log10(勒克斯) + 1 |
| 压力 | Pressure Measurement（0x0403），`MeasuredValue`（0x0000） | Float（kPa） | 什么也不做：只读 | -32767 到 32767，单位是十分之一千帕；读数为 -3276.7 到 3276.7 kPa |
| 占用 | Occupancy Sensing（0x0406），`Occupancy`（0x0000） | Boolean | 什么也不做：只读 | 有人时为 true |
| 状态 | Boolean State（0x0045），`StateValue`（0x0000） | Boolean | 什么也不做：只读 | true 或 false |

规范以固定单位发送温度、湿度和压力：百分之一摄氏度、百分之一个百分点和十分之一千帕。设备发现一开始就以 °C、% 和 kPa 提供这些值：点位到达时，量程换算环节里已经带着系数（温度和湿度为 0.01，压力为 0.1）和单位，所以设备发送 2150 时读作 21.5 °C。写入会在值发出之前把这个环节反过来算，所以写成 21.5 的设定值以 2150 到达设备。照度是对数值（10000 × log10(lux) + 1），没有哪个系数能换算它：它按设备发送的数字来读。其他所有值也都按设备发送的样子来读。

门锁、按 X 和 Y 坐标定的颜色、场景、组以及 `Toggle` 命令都不在表里。Matter 设备也没有自己的命令：每一次改动都是写它的某个点位。

## 写入一个值 {#writing-a-value}

访问为读写的 Matter 点位在被写入时会改变设备：无论写入来自它的“实时数值”卡片、仪表盘控件、Logic 还是代理。写入和任何驱动一样进入设备的队列，与设备的读取轮流进行，得到的回答是设备自己的回答。驱动根据这张表选择 Matter 命令或属性写入；没有人去选命令。只有设备回答 SUCCESS 才算写入成功。其余一律拒绝，并给出原因：

| 发生了什么 | 在哪里判定 | 给出的原因 |
|---|---|---|
| 这个点不是驱动表里的值 | 发送任何东西之前 | 驱动不知道设备怎么改变这个值，所以这个点位只读它。 |
| 这个点是表里标为只读的值 | 发送任何东西之前 | 设备报告这个值，外面没有东西去设它。 |
| 写入的值既不是 true 也不是 false（对 Boolean 值），或者不是整数 | 发送任何东西之前 | 这个值接受什么，以及写进来的是什么。 |
| 值超出规范规定的范围 | 发送任何东西之前 | 上表给出的范围。 |
| 点位没有指定簇 | 发送任何东西之前 | Matter 的值要靠输出、簇和属性来找。 |
| 设备用 SUCCESS 以外的任何状态回答 | 设备 | 设备给出的原样状态：它在规范里的名称和编号，例如 CONSTRAINT_ERROR, 0x87（值超出了设备接受的范围）或 UNSUPPORTED_COMMAND, 0x81（设备不接受改变这个值的命令）。簇附加的状态跟在后面（FAILURE, 0x01, cluster 0x02），规范没有命名的状态只读作它的编号。 |
| 设备没有及时回答 | 设备 | 无应答。 |

被拒绝的写入绝不会报成已写入，也不会报成连不上现场设备。在点位页面上，设备拒绝时状态行写“现场设备拒绝了写入：CONSTRAINT_ERROR, 0x87。”；在发送任何东西之前就断定的拒绝，状态的位置换成原因；没有收到答复时写“写入未得到确认：现场设备没有及时应答。”；“写入失败：无法连上现场设备。”留给没有连上的设备。原因也随写入一起传递：这次写入的日志行连同状态的含义写明原因，代理的 `tag_write` 也会把原因返回。

## 手工创建的点位 {#a-tag-made-by-hand}

对于设备没有以扫描能提供的形式公开的少见的值，在 Matter 设备上点**添加点位**会创建一个要手工填写的点位。它的“信号源”卡片把三个字段排在一行，下面的地址预览把它们按十进制拼成 `<endpoint>/<cluster>/<attribute>`（`1/8/0` 是第一个输出的亮度）。选好簇之前，这个点位不会保存，面板会说：“请选择这个点位读取的簇。没有簇，就不会向设备请求任何东西。”

| 字段 | 作用 | 取值 | 默认值 |
|---|---|---|---|
| **输出（endpoint）** | 点位读设备的哪一个输出。 | 0 到 65535；1 是第一个可控的输出，0 是节点本身 | 1 |
| **Matter 簇** | 点位读这个输出上的哪一个簇。 | Matter 目录里的簇，按规范名称加编号列出，比如 `Level Control (0x0008)`；目录里没有名称、由扫描找到的簇按编号排在最前面 | 无 |
| **属性** | 点位读这个簇的哪一个属性。 | 0 及以上；0 是这个簇自己的值 | 0 |

点位名称随意：它只给点位取名，不在设备上选择任何东西。数据类型从 Boolean、Int32、UInt32、Float、Double 和 String 中选；声明的类型表示不了的读数读作报错，而不是给一个凑数的数字。值按设备发送的样子到达：手工创建的温度、湿度或压力点位读作百分之一或十分之一的单位，直到你在它的量程换算环节里填上上表的系数和单位。

访问字段只在驱动的表会改变这个值的地方提供写入；否则只提供只读，并在下面说明原因：

| 点 | 访问字段提供 | 说明的话 |
|---|---|---|
| 表里由命令或属性写入改变的值 | 只读、只写和读写 | 无 |
| 还没有选簇 | 只读 | “在点位指向设备的某个值之前是只读的：请选择它的簇。” |
| 表不认识的值 | 只读 | “只读：驱动不知道设备如何改变这个值。” |
| 表标为只读的值 | 只读 | “只读：这个值由设备报告，外部不能设定它。” |

对输出、簇或属性的编辑让点位指向驱动无法写入的值时，它的访问回到只读。设备发现提供的点位保留设备发现给它的访问，因为只有设备发现会询问设备接受哪些命令：设备没有列出的值保持只读，而字段仍然提供写入。

## 早期版本保存的点位 {#tags-saved-by-earlier-versions}

早期版本只用点位名称来指定 Matter 点位的簇，地址是 `<endpoint>/<attribute>`。本版本第一次启动时，凡是名称是 Matter 目录里某个簇的 Matter 设备点位（看最后一个冒号后面的部分，去掉两边的空格，不区分大小写），都会得到这个簇，于是它继续读和以前完全一样的值。它的名称保持原样，地址预览随后显示 `<endpoint>/<cluster>/<attribute>`。名称对不上任何簇的点位从来没读到过东西：它保持没有簇，读取会被拒绝并说明原因，它的页面会要求选择簇。转换后的点位保留原来的访问，即只读，那也是那些版本唯一提供的访问；要写它，就选读写，这对上表里的值有效。
