Skip to content

设备映射管理 ​

面向开发者与配置维护者 — 如需了解控制台中的映射操作,请参考米家网关与映射

本文档描述 Olink 设备映射的核心概念、配置格式和设计约定,是自定义设备类型映射的技术参考。

架构概览 ​

Olink 将米家 MIoT 设备桥接到 HomeKit(HAP)/Matter 时,经历三层映射,职责分明:

MIoT 设备 ──→ Input 层 ──→ Schema 层 ──→ Output 层 ──→ HAP/Matter
             (属性映射+值转换) (类型/格式)    (特征声明)
  • Input 层(mapping/system/inputs/mijia/device/*.toml):MIoT spec → schema 属性映射 + 值转换
  • Schema 层(mapping/system/schemas/):属性类型、服务类型定义
  • Output 层(mapping/system/outputs/hap/*.toml):schema → HAP 特征声明(不含值转换)

配置格式 ​

Input 层使用 TOML 格式,文件路径决定加载优先级:

  • routing.toml — 分流规则(按 device_type 选择 profile)
  • device/*.toml — 设备类型映射配置
  • model/*.toml — 型号级映射配置

注意:custom 作用域的配置文件会完全替换 system 作用域中同名文件的配置。

分流规则 routing.toml ​

routing.toml 位于 mapping/system/inputs/mijia/routing.toml,用于在多个设备 profile 之间进行分流。规则按声明顺序匹配,第一条命中的 profile 生效;未命中任何规则时 fallback 到 device/<device_type>.toml。

toml
[[rule]]
device_type = "fan"
profile = "fan.heater"
when = { has_service = "heater" }

条件表达式 ​

when 字段支持以下条件表达式:

表达式说明示例
has_service设备存在指定 MIoT 服务{ has_service = "heater" }
has_property设备存在指定属性{ has_property = "on" } 或 { service = "light", property = "brightness" }
all全部条件满足{ all = [{ has_service = "a" }, { has_service = "b" }] }
any任一条件满足{ any = [{ has_service = "a" }, { has_service = "b" }] }
not取反{ not = { has_service = "light" } }

条件表达式支持递归嵌套,如:

toml
when = { all = [
    { has_service = "speaker" },
    { not = { has_service = "light" } },
] }

Input 层基本结构 ​

toml
device_type = "air-purifier"

[services.air-purifier]
read = { strategy = "auto", stale_threshold = 600, jitter_ratio = 0.2 }

[services.air-purifier.attributes]
on = { source = "active", service = "air-purifier" }
  • device_type 是设备类型标识(如 "light"、"fan")
  • [services.<service_name>] 定义每个服务的配置,service_name 对应 schema 服务名
  • read 定义读取策略
  • [services.<service_name>.attributes] 定义属性映射

属性映射格式 ​

1:1 简单映射 ​

toml
[services.light.attributes]
power-state = { source = "on", service = "light" }
brightness = { source = "brightness", service = "light" }

每个属性的 key 是目标 schema 属性名,value 中的 source 是 MIoT 源属性名,service 指定 MIoT 服务名。

1:1 带转换 ​

toml
[services.air-purifier.attributes.mode]
schema_attr = "target-air-purifier-state"
transform = { kind = "enum_map", to_map = [
    { from = 0, to = 1 },
    { from = 1, to = 0 },
], from_map = [
    { from = 0, to = 2 },
    { from = 1, to = 0 },
] }

约定:Input 层一般不做值转换,转换应在 Output 层完成。仅在 Schema 层需要特定格式时才在 Input 层转换。

1:N 多映射(Combine) ​

同一个 MIoT 属性拆解为多个 schema 属性,再通过 type = "combine" 虚拟属性合并:

toml
# 色相:从颜色属性读取,带枚举映射转换
hue = { source = "color", service = "light", transform = { kind = "enum_map", to_map = [
    { from = 0, to = 2 },
    { from = 1, to = 1 },
    { from = 2, to = 0 },
] } }

# 饱和度:从颜色属性读取,带枚举映射转换
saturation = { source = "color", service = "light", transform = { kind = "enum_map", to_map = [
    { from = 0, to = 2 },
    { from = 1, to = 1 },
    { from = 2, to = 0 },
] } }

# 色彩RGB:虚拟属性,通过 hue + saturation 合并生成 color-rgb 值
color-rgb = { type = "combine", inputs = ["hue", "saturation"], strategy = { type = "native", kind = "color_hs" } }

type = "combine" 的虚拟属性不绑定真实 MIoT 属性,其 inputs 列出需要合并的 schema 属性名,strategy 定义合并策略。

同一 MIoT 服务分发到不同 schema 服务 ​

当一个 MIoT 服务的属性需要分发到多个 HAP 服务时,在多个 [services.<name>] 块中分别指定 source 和 service:

toml
# environment 的 PM2.5 → air-purifier 服务
[services.air-purifier.attributes]
"air-particulate-density" = { source = "pm2.5-density", service = "environment" }

# environment 的 temperature → temperature-sensor 服务
[services.temperature-sensor.attributes]
temperature = { source = "temperature", service = "environment" }

Output 层 TOML 格式 ​

文件位置:mapping/system/outputs/hap/*.toml

toml
schema_id = "air-purifier"

[primary_service]
hap_service = "AirPurifier"
schema_service = "air-purifier"

[[characteristics]]
hap_type = "Active"
schema_attr = "active"
min = 0
max = 1

[[characteristics]]
hap_type = "TargetAirPurifierState"
schema_attr = "target-air-purifier-state"
min = 0
max = 1
transform = { kind = "enum_map", to_map = [
    { from = 0, to = 1 }, # Auto → Automatic
    { from = 1, to = 0 }, # Sleep → Manual
], from_map = [
    { from = 0, to = 2 }, # Manual → Favorite
    { from = 1, to = 0 }, # Automatic → Auto
] }

[[characteristics]]
hap_type = "LockPhysicalControls"
schema_attr = "lock-physical-controls"
min = 0
max = 1
optional = true
  • characteristics 列表决定哪些 schema 属性暴露到 HomeKit
  • transform 在此层定义值转换
  • optional = true 表示该特征非必需

Schema 层 ​

Schema 层定义属性类型、格式和访问权限,位于 mapping/system/schemas/:

toml
# schemas/attributes/lock-physical-controls.toml
[attribute]
id = "lock-physical-controls"
name = "锁定物理控制"
format = "bool"
access = ["read", "write", "notify"]

[attribute.mapping.hap]
type = "LockPhysicalControls"
service = "HeaterCooler"

Transform 类型 ​

定义在 olink-core/src/mapping/schema/attribute.rs 的 TransformConfig 枚举:

kind说明示例
enum_map枚举映射{ from = 0, to = 1 }
piecewise分段映射离散值精确匹配
range区间映射连续值映射到离散区间
linear线性插值source_min/max → target_min/max
threshold阈值判断v > 20 → true
constant常量始终返回固定值
jsJS 脚本复杂逻辑
reciprocal反比factor / v(如 Kelvin↔mired)

enum_map 详解 ​

toml
transform = { kind = "enum_map", to_map = [
    { from = 0, to = 1 },
    { from = 1, to = 0 },
], from_map = [
    { from = 0, to = 2 },
    { from = 1, to = 0 },
] }
  • to_map:正向转换(读侧,MIoT → HAP)
  • from_map:反向转换(写侧,HAP → MIoT)。未提供时自动反转 to_map
  • to_default / from_default:未命中时的默认值

piecewise 分段映射 ​

适用于离散值精确映射:

toml
transform = { kind = "piecewise", segments = [
    { from = 0, to = 0 },
    { from = 1, to = 33 },
    { from = 2, to = 66 },
    { from = 3, to = 100 },
], from_map = [
    { from = 0, to = 0 },
    { from = 33, to = 1 },
    { from = 66, to = 2 },
    { from = 100, to = 3 },
] }

range 区间映射 ​

适用于将连续值映射到离散区间(如风扇档位):

toml
transform = { kind = "range", min = 1, ranges = [
    { max = 1, to = 25 },
    { max = 2, to = 50 },
    { max = 3, to = 75 },
    { max = 4, to = 100 },
], default = 1 }
  • ranges 按 max 升序排列,每个区间为 (prev_max, max]
  • 第一个区间的下界为 min
  • 超出所有区间范围时返回 default

linear 线性插值 ​

toml
transform = { kind = "linear", source_min = 0, source_max = 100, target_min = 0, target_max = 100 }

reciprocal 反比 ​

适用于色温映射(Kelvin ↔ mired):

toml
transform = { kind = "reciprocal", factor = 1000000 }

设计约定 ​

  1. Input 层负责属性映射:MIoT 属性名 → schema 属性名的映射在 Input 层完成
  2. Output 层负责值转换:MIoT 值域 → HAP 值域的映射在 Output 层完成
  3. spec_type 是设备级映射:只包含标准服务
  4. 1:N 映射用 Combine:同一 MIoT 属性拆解为多个 schema 属性时使用 type = "combine"
  5. read 策略:每个 [services.<name>] 块应有 read 配置
  6. HAP output 只做特征声明:声明哪些 schema 属性暴露到 HomeKit
  7. custom 替换 system:custom 作用域的配置文件完全替换 system 中同名配置

读策略配置 ​

toml
[services.fan]
read = { strategy = "auto", stale_threshold = 600, jitter_ratio = 0.2 }
  • strategy = "auto":根据事件频率自动切换
  • strategy = "poll":定时轮询
  • strategy = "passive":仅被动接收推送

典型案例 ​

physical-controls-locked(童锁) ​

场景:某些空气净化器型号(如 zhimi-ma2)有 physical-controls-locked 服务,MIoT 属性 physical-controls-locked 映射到 schema 的 lock-physical-controls。

Input 层(air-purifier.toml):

toml
[services.air-purifier.attributes]
"lock-physical-controls" = { source = "physical-controls-locked", service = "physical-controls-locked" }

Output 层(outputs/hap/air-purifier.toml):

toml
[[characteristics]]
hap_type = "LockPhysicalControls"
schema_attr = "lock-physical-controls"
min = 0
max = 1
optional = true

环境传感器多路分发(Multi-service) ​

场景:空气净化器的环境传感器同时提供 PM2.5 和温度数据,需要分发到不同的 HAP 服务。

toml
# environment → air-purifier(PM2.5)
[services.air-purifier.attributes]
"air-particulate-density" = { source = "pm2.5-density", service = "environment" }

# environment → temperature-sensor(温度)
[services.temperature-sensor.attributes]
temperature = { source = "temperature", service = "environment" }

枚举值转换 ​

场景:空气净化器模式从 MIoT 到 HAP 需要 enum 值转换。

toml
[services.air-purifier.attributes.mode]
schema_attr = "target-air-purifier-state"
transform = { kind = "enum_map", to_map = [
    { from = 0, to = 1 },
    { from = 1, to = 0 },
], from_map = [
    { from = 0, to = 2 },
    { from = 1, to = 0 },
] }