Repository navigation
Controls
Documentation home · All topics · Source
| Control | Portal widget |
|---|---|
| slider | Numeric slider |
| text | Read-only measurement or text |
| select | Dropdown selection |
| edit | Editable text |
| switch | On/off toggle |
| climate | Thermostat dial |
| binary_sensor | Binary status indicator |
| date | Date input |
| time | Time input |
| datetime | Date and time input |
| button | Single action |
| scene | Scene action |
| notify | Text notification action |
| infrared | JSON infrared action |
| event | Event publication |
| device_trigger | Automation trigger |
| tag | Scanned tag publication |
| device_tracker | Presence state |
| light | Power, brightness and RGB |
| fan | Power, speed and oscillation |
| cover | Opening, position and tilt |
| valve | Opening and position |
| lock | Lock actions |
| alarm_control_panel | Alarm actions |
| humidifier | Humidity dial and modes |
| siren | Power, tone, duration and volume |
| vacuum | Vacuum actions and speed |
| lawn_mower | Mower actions |
| water_heater | Water temperature and modes |
| update | Firmware installation action |
The complete current Zigbee coverage table marks which fields are native, composite (several separate endpoints), generic single-endpoint controls, or unmapped. All 30 types remain documented individually.
Control types can be enabled or disabled at compile time with defines. ARDPORTAL_ENABLE_CONTROLS selects the default for optional types; an individual flag such as ARDPORTAL_ENABLE_CONTROL_LIGHT overrides that type. See Defines → Optional field types for every flag and its default. Set the flags before #include <ArdPortal.h> in the main sketch, then rebuild/upload the firmware. Dynamic portal pages also require ARDPORTAL_ENABLE_DYNAMIC_PAGES.
There are 30 field types, presented in one catalogue below. Each screenshot shows the actual library portal renderer with illustrative sample values, captured in a local browser preview. These are portal screenshots, not Home Assistant cards or evidence of connected hardware.
The examples below describe the built-in presets shipped with this library,
not every capability that Home Assistant may support. Register a field with its
id, type and name; add only the options you need. Field IDs are stable
application keys, not C++ variable declarations.
Each control page also describes Integration with Home Assistant and Integration with Zigbee, including the behavior ArdPortal implements and the remaining gaps. Possible future cluster combinations are clearly marked as unimplemented; see the Zigbee feasibility table.
Every linked control page includes minimal JSON and full JSON, both ready to copy into a page's fields array. Minimal JSON contains only the properties needed to register the field. Full JSON makes the example configuration explicit, including defaults and the built-in controls and ha settings where applicable; it is not a requirement to supply every optional property. The property table on each control page identifies what can be omitted. $state, $command and $command:<suffix> are topic placeholders expanded by the library. Internal normalization properties are not needed in your JSON.
A page containing one or more of these objects has this structure:
{
"id": "settings",
"name": "Settings",
"order": 10,
"fields": [
{"id": "enabled", "type": "switch", "name": "Enabled", "default": false}
]
}Each field ID must be unique across registered pages. See Dynamic pages for page registration.
id and type are required for every field. A display title is also required: the examples use name; you may replace it with a nonempty names translation object. names is optional when name is present. The slider and climate fields also require min and max; select requires options. Other types obtain their default state and control/discovery definitions from the library presets.
Optional properties available across the catalogue:
| Property | Required? | Meaning when supplied / omitted |
|---|---|---|
names |
Optional with name
|
Translated titles; see Languages. Without it, the default title is used. |
default |
Optional | Initial value. Omission uses the type's default; it does not remove the field. |
persist |
Optional | Storage policy. Omission uses the type policy; false keeps edits in RAM. See Persistence and callbacks. |
visibleWhen |
Optional | Visibility rule; without it, there is no field-specific condition. Page conditions still apply. See Dependent fields. |
icon |
Optional | HA icon override. The preset/HA default is used when omitted. |
ha |
Optional | Overrides only supplied discovery properties. Omission uses automatic HA configuration, including topics and templates. |
unit_of_measurement, device_class, state_class, entity_category
|
Optional, where supported by the HA component | Measurement/classification metadata. Choose values appropriate for that component. |
For fields with preset controls, the entire array is optional. If supplied, it replaces the preset array. Its entries must contain the properties required by their control type, such as a slider's range or an action's payload; see Custom controls. Optional ha overrides must remain consistent with those controls. For select, the options array is required, and each option requires value plus a title (name or names). For grouped scenes, group is optional; when supplied it needs id and a title. These are requirements inside an optional customization, not additional requirements for minimal field JSON.
Some field types use built-in control descriptors. Unlike a select field, descriptor-based
select controls store their allowed values as an array of strings in controls.
Composite values must include all properties present in their declared default.
Changing one property through the portal preserves the remaining properties.
Additional telemetry properties are allowed, within the 1024-byte extended
composite-state limit. The application is responsible for operating hardware.
button, scene, notify and infrared keep null as their state/default. They invoke
onAppConfigCommand; they are not persistent switches and are not periodically replayed.
Action submission requires an installed onAppConfigCommand callback. Commands submitted
from the portal execute locally even when MQTT is disconnected. A callback returning
false rejects the command; returning true acknowledges acceptance, not physical
completion. This behavior also applies to action buttons in composite devices.
All use the base command topic.
Action-text and JSON inputs require an explicit Send. This avoids transmitting an action on every keystroke. Action JSON is bounded to 1500 serialized bytes with at most 512 nodes; full MQTT packet limits still apply.
event, device_trigger and tag are read-only in the portal. Use emitAppConfigEvent for an occurrence,
not a persistent setter: events are nonretained, queued only while MQTT is
connected, and are not replayed on reconnect or during periodic refresh.
ha overrides discovery properties; controls replaces the entire built-in
control array rather than merging entries by key. default describes the complete
initial state. Keep all three consistent. The slider field declares its range directly; composite ranges and mode lists
need explicit control overrides.
Reserved discovery identity/name/device/availability properties are managed by
the library. $state, $command and $command:<suffix> are expanded in direct
string-valued ha properties, not recursively inside arbitrary nested JSON.
A control's key selects a property in a composite state; empty key selects the
whole scalar value. command is a suffix, without the leading underscore; empty
command uses the base field topic. name and optional names customize portal
labels; label can reference a catalog UI key. The available control descriptors
are:
Control type
|
Additional properties | Behavior |
|---|---|---|
slider |
min, max, positive step
|
Numeric range with live readout; values must match the range and step. |
switch |
Optional string on/off
|
Boolean by default, or the configured string values; rendered as a graphical switch. |
select |
options: ["eco","normal"] |
String dropdown; this descriptor syntax differs from a select field's translated option objects. |
color |
None | RGB object with integer r, g, b channels, each 0–255. |
edit |
None | Editable string. |
date, time, datetime
|
None | Strings following the formats listed above. |
action |
payload |
Button invoking onAppConfigCommand with the declared payload. |
action_text |
None | Text input and explicit Send; invokes onAppConfigCommand. |
action_json |
None | JSON input and explicit Send; invokes onAppConfigCommand. |
For example, a humidifier with a narrower target range and a custom mode:
{
"id":"room_humidifier","type":"humidifier","name":"Room humidifier",
"default":{"state":"OFF","humidity":45,"current_humidity":null,"mode":"quiet"},
"ha":{"min_humidity":30,"max_humidity":70,"modes":["quiet","normal"]},
"controls":[
{"key":"state","type":"switch","name":"Power","command":"","on":"ON","off":"OFF"},
{"key":"humidity","type":"slider","name":"Humidity","command":"humidity","min":30,"max":70,"step":1},
{"key":"mode","type":"select","name":"Mode","command":"mode","options":["quiet","normal"]}
]
}Changing a preset without matching its HA topics/templates can produce a portal
that works locally but does not communicate correctly with HA. The full demo in
the external ArdUI.ino demo and examples/DynamicPages are starting points; use actual hardware
feedback in production. The library does not enforce a whitelist of physical
operating states for action-only devices; the state examples above identify the
values recognized by the shipped portal button highlighting.
The portal renders humidifier with the same circular dial as climate, using
percent humidity instead of temperature. Drag the dial, use the plus/minus
buttons or the arrow keys to change its target. Power and mode controls remain
available; updates from MQTT immediately refresh the widget. water_heater uses
the same dial for target temperature, displays current temperature and retains
its mode selector. All three dials mark the current measurement with a dot:
the arc is light below the lower value and darker between current and target
when the target is higher. Above the target the arc is gray; a current
measurement above the target is marked with a gray dot. The current marker is hidden when no measurement is known. In off mode, the
whole arc, target handle, current marker and selected off button are gray.
Cooling reverses the arc: gray below the target, dark blue from the target to
a higher current temperature, then light blue up to the maximum.
Both widgets respect the range and step of their slider
control definitions.
Action controls for cover, valve, lock, alarm_control_panel, vacuum
and lawn_mower use rounded buttons in a wrapping row. A green button reflects
the reported device state; clicking an action does not assume it succeeded.
Lock, Alarm Control Panel, Vacuum and Lawn Mower hide raw state strings; their selected buttons show
the reported state. The portal refreshes values after command completion as well
as on WebSocket updates. The main sketch simulates reported Lock/alarm/robot states;
hardware integrations must report the actual result with setAppConfigStateValue.
Sliders, selectors and custom-command inputs remain available below the row.
binary_sensor is displayed as a read-only status badge with a colored dot and
a localized Active/Inactive label. Missing measurements show Unknown state.
MQTT updates refresh the indicator immediately.
The field types cover the supported MQTT discovery entity
categories, including device_automation through device_trigger. Hardware
operations remain the application's responsibility. The built-in presets provide
common capabilities; advanced capabilities can be configured through ha and
controls. HA topic values can use $state, $command or $command:<suffix>;
these expand to the device's topics. Custom controls must match those options,
command suffixes and the declared state/default schema. Sensor metadata accepts
unit_of_measurement, device_class, state_class and entity_category.
Consecutive portal edits update the visible state immediately and coalesce into the pending snapshot before flash writing begins.
Editable extended fields default to durable storage (persist: true), so their
settings survive restart. Read-only telemetry and transient actions/events
default to RAM (persist: false) to avoid writing measurements to flash.
setAppConfigValue follows this policy for registered extended fields; an explicit
persist: false keeps a field volatile. This applies to form controls, composite controls, MQTT commands and setAppConfigValue(). A form containing both kinds saves only persistent fields; volatile changes remain in RAM. Forms containing only volatile fields do not start a storage transaction. Editable fields without persist retain their default durable behavior. setAppConfigStateValue always updates RAM only. Its optional third argument defaults
to true (publish changed state to MQTT). Pass false for local live updates
without queuing MQTT; use queueAppConfigStatePublish(key) to queue the latest value
through the cooperative publisher, even if unchanged. This returns queue success,
not broker confirmation. Periodic refresh and reconnect/get snapshots still apply;
set Options::appStateIntervalMs=0 when managing your own refresh schedule. All
registered fields are read through getAppConfigValue.
portal.setAppConfigStateValue("temperature", 22.5); // Telemetry: no flash write.
portal.onAppConfigCommand([](const String& field, const String& command,
const ArdJSON::JSONVar& value, ArdPortal::ChangeSource source) {
// Accept/queue a nonblocking hardware action; return false if rejected.
return true;
});
portal.emitAppConfigEvent("tag_reader", "tag-id"); // With MQTT enabled, connection required.Actions require onAppConfigCommand. When MQTT is connected, accepted actions attempt to
queue a nonretained acceptance message containing accepted, command and value;
stateful entities also attempt to queue their reported state. Disconnection or a
full emission queue does not reject an action accepted by the callback.
Report actual hardware results with setAppConfigStateValue.
With MQTT enabled, events and action replies share an eight-message FIFO; emission
returns false when full or disconnected. Without MQTT, supported events update
local RAM state/revision only. emitAppConfigEvent() does not invoke the application
value-change callback. Events/actions are never retained or replayed periodically.
Binary console payloads are
summarized by byte count; they are not rendered as text.
See the official MQTT discovery documentation
and MQTT climate documentation.
Discovery tests use a simulated broker; physical device/HA interoperability still
requires testing with your installation. Generating/parsing JSON, callbacks and
existing core flash/TCP/TLS operations execute synchronously; network sends,
discovery refresh and storage work are scheduled incrementally by loop().
| ArdPortal field | HA component |
|---|---|
slider |
number |
text |
sensor |
select |
select |
edit |
text |
switch, climate
|
Same component name |
device_trigger |
device_automation |
| Other descriptor-based types | Matching component name |
The portal widget and the HA widget are separate UIs. A light advertises RGB
and brightness, so HA can show its native light color picker. It is not a
standalone HA color entity. A switch's mdi:toggle-switch icon does not change
HA's assumed-state behavior: an explicit ha.optimistic:true can still make HA
show two action buttons instead of one state toggle.
ArdPortal · ArdFS · ArdJSON — Source · Releases · MIT license