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
# devices.toml — source of truth for device capabilities
#
# Each entry maps a real device model to a DeviceKind and the set of
# features denki supports on that kind.
#
# Capability consistency tests in src/app/tests/capabilities.rs parse this
# file (via src/devices/registry.rs) and verify that:
# 1. Every feature listed here is permitted by the corresponding can_* guard.
# 2. Every guarded feature NOT listed here is denied by the guard.
#
# When you add or change a guard in src/devices/guards.rs, update this file
# to match. When you add a new device model, add an entry here.
#
# Guarded features (each maps to a can_* function in src/devices/guards.rs):
# power — can_control_power(kind)
# dim — can_dim(kind)
# color_temp — can_set_color_temp(kind)
# color — can_set_color(kind)
# specs — can_get_specs(kind)
# presets — can_get_presets(kind)
# schedules — can_get_schedules(kind)
# led — can_control_led(kind)
# clock — can_get_clock(kind)
#
# Informational features (listed in supports; not enforced by the
# capabilities consistency test's GUARDED set):
# energy — available on Bulb/LightStrip always; Plug/Strip when ENE is set
# outlets — Strip kind only
# effects — LightStrip only (also has can_get_effects; used by handlers)
# ── Bulbs ─────────────────────────────────────────────────────────────────────
[[]]
= "KL135"
= "Bulb"
= true
= ["power", "dim", "color_temp", "color", "energy", "specs", "presets"]
# schedules / led / clock not present on KL135
[[]]
= "LB130"
= "Bulb"
= false
= ["power", "dim", "color_temp", "color", "energy", "specs", "presets"]
# older generation; same guard profile as KL135
# ── Light strips ──────────────────────────────────────────────────────────────
[[]]
= "KL430"
= "LightStrip"
= false
= ["power", "dim", "color_temp", "color", "energy", "effects"]
[[]]
= "KL420L5"
= "LightStrip"
= false
= ["power", "dim", "color_temp", "color", "energy", "effects"]
# light-strip support matches the KL430 namespace
# ── Dimmers ───────────────────────────────────────────────────────────────────
[[]]
= "HS220"
= "Dimmer"
= false
= ["power", "dim", "schedules", "led", "clock"]
# ── Plugs ─────────────────────────────────────────────────────────────────────
[[]]
= "KP115"
= "Plug"
= true
= ["power", "energy", "schedules", "led", "clock"]
[[]]
= "HS110"
= "Plug"
= true
= ["power", "energy", "schedules", "led", "clock"]
[[]]
= "HS105"
= "Plug"
= true
= ["power", "schedules", "led", "clock"]
# no energy chip — feature string is "TIM", not "TIM:ENE"
# ── Power strips ──────────────────────────────────────────────────────────────
[[]]
= "HS300"
= "Strip"
= true
= ["power", "energy", "schedules", "led", "clock", "outlets"]
# Verified on HS300(US) HW 2.0 / FW 1.1.2
# HW 2.0 omits relay_state from sysinfo (defaulted) and uses short child IDs ("00"–"05")
# that must be prefixed with deviceId for per-outlet commands
[[]]
= "KP303"
= "Strip"
= false
= ["power", "schedules", "led", "clock", "outlets"]
# KP303 typically lacks energy monitoring (feature string is "TIM", not "TIM:ENE")
# ── Tapo devices (KLAP protocol) ──────────────────────────────────────────────
[[]]
= "P125"
= "Tapo"
= "klap"
= true
= ["power"]
# Auto-adapter info and on/off readbacks verified on HW 1.0,
# FW 1.4.0 Build 260803 Rel.232153; toggle remains unverified.
= ["power"]
= false
# Original Tapo entries select KLAP; auto-adapter capabilities are separate.
[[]]
= "P110"
= "Tapo"
= "klap"
= false
= ["power", "energy"]
[[]]
= "P115"
= "Tapo"
= "klap"
= false
= ["power", "energy"]
[[]]
= "KP125M"
= "Tapo"
= "klap"
= false
= ["power", "energy"]
[[]]
= "P125M"
= "Tapo"
= "klap"
= false
= ["power"]