TapHome

Shelly DUO

Packet Parser → HTTP
Submitted by
Last updated: 03. 2026
Shelly DUO

The Shelly DUO is a Wi-Fi LED bulb with an E27 base that supports brightness dimming and tunable white color temperature (CCT) in the range of 2700 K (warm white) to 6500 K (daylight). TapHome integrates with the bulb locally over Wi-Fi using either the HTTP REST API or MQTT. No cloud connection is required.

Two TapHome templates are available: an HTTP template for a single bulb, and an MQTT template that can control up to 5 DUO bulbs through a single MQTT broker connection. Both templates expose the same device types per bulb — a light device with brightness and CCT control, and an electric meter for power monitoring.

Configuration

HTTP template

The HTTP template requires the bulb’s IP address (or mDNS hostname ShellyBulbDuo-{MAC}.local). TapHome polls the /status endpoint every 2.5 seconds and caches the JSON response into the jsonStatus custom variable. Individual device scripts parse the cached response without making redundant HTTP calls.

The template accepts two import parameters:

  • IP Address — the bulb’s local IP (default 192.168.0.1)
  • Transition time — light transition duration in milliseconds (default 300 ms)
MQTT template

The MQTT template requires the broker IP, port, and the Device ID for each bulb (format: ShellyBulbDuo-DEVICEID). The Device ID can be found in the Shelly web UI under Settings > Device Info, or via GET http://{bulb-ip}/settings in the mqtt.id field.

The template supports up to 5 DUO bulbs through a single module. Each bulb is identified by its own topic variable (bulb1topic through bulb5topic). Unused bulb slots can be left at their default placeholder value.

The template accepts three import parameters:

  • MQTT Broker IP — the broker address (default 192.168.0.1)
  • MQTT Broker port — the broker port (default 1883)
  • Transition time — light transition duration in milliseconds (default 300 ms)

On Gen1 Shelly devices, enabling MQTT disables Shelly Cloud. Both cannot run simultaneously.

Device capabilities

Light control

The template exposes the DUO as a white light device with two controllable parameters:

  • Brightness — read from lights[0].brightness (0-100, converted to 0.0-1.0 internally) and lights[0].ison; controlled via /light/0?turn=on|off&brightness=N (HTTP) or by publishing JSON to the light/0/set topic (MQTT)
  • Color temperature — read from lights[0].temp (2700-6500 K); controlled via /light/0?temp=K (HTTP) or the temp field in the JSON payload (MQTT)

Both parameters are sent together in a single write command. The transition time parameter controls the fade duration for all changes.

When the light is turned off (brightness set to 0), the template sends turn=off (HTTP) or publishes off to the light/0/command topic (MQTT).

Debounce mechanism

The HTTP template includes a debounce guard to prevent stale readings during light transitions. After every write command, the template ignores /status reads for the duration of the transition time plus 500 ms. During this period, the read scripts return the last known values instead of polling the device. This prevents the UI from briefly showing intermediate states while the bulb is transitioning.

The MQTT template does not need debounce — it receives state updates directly from the bulb as they occur.

Power metering

The built-in energy meter reads two values:

  • Real-time powermeters[0].power in watts, converted to kW (divided by 1000)
  • Total consumptionmeters[0].total in watt-minutes, converted to kWh (divided by 60 000)

The meter is read-only and updates automatically with every poll cycle (HTTP) or on each MQTT message.

To enable power monitoring, the device model must be configured in the Shelly app or web browser under Settings > Device Model. If the bulb is on but power reads zero, the template displays a warning about this configuration step.

Service diagnostics (HTTP template)

The HTTP template exposes 10 service attributes:

  • Network info — IP address, MAC address (formatted with colon separators), WiFi signal strength (dB)
  • Connectivity — cloud enabled/connected, MQTT connected
  • Device info — device time, uptime (formatted as days/hours/minutes), RAM usage
  • Firmware — firmware update available
Service actions (HTTP template)

Two service actions are available:

  • Enable cloud — enables or disables Shelly Cloud connectivity
  • Reboot — triggers a device reboot
Additional capabilities

The Shelly DUO also exposes a light timer (active flag and remaining seconds), an auto-off timer on the light endpoint, internal device temperature, WiFi signal strength as a standalone sensor device, and an MQTT online/offline LWT topic for connection status detection. These capabilities can be added in a future template update.

Troubleshooting

Bulb not responding (HTTP)
  1. Verify the DUO is connected to Wi-Fi and has a valid IP address
  2. Try using the mDNS hostname (ShellyBulbDuo-AABBCCDDEE.local) instead of the IP address — the IP may have changed after a DHCP renewal
  3. Open http://{bulb-ip}/shelly in a browser — if it responds with a JSON containing "type":"SHBDUO-1", the bulb is reachable
  4. Check that TapHome CCU and the DUO are on the same network / VLAN
Power readings show zero
  1. Confirm the device model is configured in the Shelly app (Settings > Device Model)
  2. Check that the bulb is turned on — the meter only reads when current flows
  3. Poll /status manually and verify meters[0].power returns a non-zero value
MQTT bulb not responding
  1. Verify MQTT is enabled in the Shelly web UI (Internet & Security > Advanced — MQTT)
  2. Confirm the broker address and port are correct in both the Shelly device and TapHome module settings
  3. Check that the bulbNtopic custom variable matches the bulb’s Device ID exactly (e.g., ShellyBulbDuo-B929CC)
  4. Use an MQTT client (e.g., MQTT Explorer) to subscribe to shellies/# and verify the bulb publishes messages
Light transitions appear jerky
  1. Increase the transition time parameter — the default 300 ms may be too short for large brightness changes
  2. If using HTTP, ensure no other system is polling the bulb simultaneously — Gen1 devices support only 2 concurrent connections

Gen1 Shelly devices support only 2 concurrent HTTP connections. If TapHome and another system (e.g., Home Assistant) poll the same device simultaneously, communication may become unreliable. Consider switching to the MQTT template for multi-system environments.

How to install in TapHome

Prerequisites

  • Shelly device installed and powered on
  • Local Wi-Fi network (2.4 GHz)
  • TapHome CCU on the same network

Step 1 — Connect Shelly to Wi-Fi

Option A — Shelly app (recommended):

  1. Download the Shelly app (iOS / Android)
  2. Tap +Add Device and follow the Bluetooth pairing wizard
  3. Enter your Wi-Fi credentials when prompted

Option B — AP mode (no app):

  1. On first power-up the device creates a hotspot: ShellyXXX-AABBCCDDEE
  2. Connect your phone/PC to that hotspot
  3. Open http://192.168.33.1Internet & SecurityWi-Fi Mode - Client
  4. Enter SSID and password → Save

Shelly only supports 2.4 GHz networks. 5 GHz networks will not appear in the scan.

Step 2 — Find the device address

Shelly Gen1 devices support mDNS (Zeroconf). You can reach the device using a hostname instead of an IP address:

1
shelly<model>-<MAC>.local

For example: shelly1pm-AABBCCDDEE.local (MAC address in uppercase hex, no colons).

Recommended: use the TapHome IP Scanner. Open the TapHome app and use the IP Scanner (Settings → Network → Scan). The scanner will discover devices on your network and show both the IP address and the mDNS hostname. Use the hostname instead of the IP address for a more reliable connection — it stays the same even if the device’s IP changes after a router reboot.

Alternative methods to find the IP address:

  • Shelly app: Device detail → Device info → IP address
  • Shelly web UI: Connect to the device AP before Wi-Fi setup — the IP is shown after saving
  • Router DHCP table: Look for a hostname like shelly1pm-AABBCCDDEE

Step 3 — Configure in TapHome

  1. In TapHome, add a new Packet Parser (HTTP) module
  2. Address: enter the mDNS hostname (e.g., shelly1pm-AABBCCDDEE.local) or IP address from Step 2
  3. Port: 80 (default, no change needed)
  4. Import the template — TapHome will poll /status to read device state

HTTP authentication is disabled by default on Shelly devices. If you have enabled login protection, TapHome does not support HTTP Basic Auth at this time — keep auth disabled for TapHome integration.

Available devices

Shelly DUO Module
Service Attributes
IP Address
MAC Address
WIFI signal
Cloud enabled
Cloud connected
MQTT connected
Device time
FW update available
Uptime
RAM
Service Actions
Enable cloud
Reboot
Custom Variables
transitionTime (numeric) = TransitionTimeLight transition duration in milliseconds (import parameter, default 300 ms)

Shelly DUO Module

Read (module)
VAR now := NOW();

IF now.Ticks < debounceTimestamp
    RETURN(0);
END

VAR response := SENDHTTPREQUEST("/status");

IF response.IsSuccess
    jsonStatus := response.Content;
ELSE
    jsonStatus := NULL;
END
Service Attributes
IP Address
VAR response := SENDHTTPREQUEST("/status");

IF response.IsSuccess
    jsonStatus := response.Content;
ELSE
    jsonStatus := NULL;
END

VAR address := PARSEJSON(jsonStatus, "$.wifi_sta.ip", 1);
IF (ISNULL(address), "-", address)
MAC Address
VAR value := PARSEJSON(jsonStatus, "$.mac", 1);
IF ISNULL(value)
    RETURN("-");
END;

STRING mac;
INT i := 0;
INT len := LENGTH(value);

WHILE i < len
    mac += GETAT(value, i);
    i += 1;
    
    IF MOD(i, 2) = 0 AND i < len
        mac += ":";
    END
LOOP

mac
WIFI signal
VAR signal := PARSEJSON(jsonStatus, "$.wifi_sta.rssi", 1);
IF (ISNULL(signal), "-", signal + "db");
Cloud enabled
VAR value := PARSEJSON(jsonStatus, "$.cloud.enabled", 1);
IF (ISNULL(value), "-", value)
Cloud connected
VAR value := PARSEJSON(jsonStatus, "$.cloud.connected", 1);
IF (ISNULL(value), "-", value)
MQTT connected
VAR value := PARSEJSON(jsonStatus, "$.mqtt.connected", 1);
IF (ISNULL(value), "-", value)
Device time
VAR value := PARSEJSON(jsonStatus, "$.time", 1);
IF (ISNULL(value), "-", value)
FW update available
VAR value := PARSEJSON(jsonStatus, "$.has_update");
IF (ISNULL(value), "-", value)
Uptime
VAR value := PARSEJSON(jsonStatus, "$.uptime", 1);
IF ISNULL(value)
    RETURN("-");
END

VAR days := FLOOR(value/86400, 1);
value := MOD(value, 86400);
VAR hours := FLOOR(value/3600, 1);
value := MOD(value, 3600);
VAR minutes := FLOOR(value/60, 1);

days + "day(s) " + hours + "h " + minutes + "m"
RAM
VAR ramFree := PARSEJSON(jsonStatus, "$.ram_free", 1);
VAR ramTotal := PARSEJSON(jsonStatus, "$.ram_total", 1);

IF ISNULL(ramFree) OR ISNULL(ramTotal)
    RETURN("-");
END

ramFree + " bytes free of " + ramTotal
Service Actions
Enable cloud
Parameters: Enable (Enable / Disable)
VAR response := SENDHTTPREQUEST("/settings/cloud?enabled=" + enable);
VAR contentJson := response.Content;
VAR wasEnabled := PARSEJSON(contentJson, "enabled");

IF(wasEnabled, "Cloud enabled", "Cloud disabled");
Reboot
VAR response := SENDHTTPREQUEST("/reboot");
VAR contentJson := response.Content;
VAR wasRebooted := PARSEJSON(contentJson, "ok");

IF(wasRebooted, "Reboot successful", "Error");
Light White Light

CCT white light (2700-6500 K) — brightness dimming and color temperature control

numeric Unit: %, K json_path

Light

Read brightness
VAR now := NOW();

IF now.Ticks < debounceTimestamp
    RETURN(Hb);
END

VAR value := PARSEJSON(jsonStatus, "$.lights[0].brightness", 1);

IF ISNULL(value)
    RETURN(NaN);
END

BOOL isOn := PARSEJSON(jsonStatus, "$.lights[0].ison", 1);

IF ISNULL(isOn)
    RETURN(NaN);
END

IF(isOn, value/100.0, 0);
Write brightness
IF St > 0.5
    SENDHTTPREQUEST("light/0?turn=on&transition=" + transitionTime + "&brightness=" + ROUND(Hb*100.0));
ELSE
    SENDHTTPREQUEST("light/0?turn=off&transition=" + transitionTime);
END

VAR now := NOW();
debounceTimestamp := now.Ticks + transitionTime + 500;
Read color temperature
VAR now := NOW();

IF now.Ticks < debounceTimestamp
    RETURN(Ct);
END

VAR value := PARSEJSON(jsonStatus, "$.lights[0].temp", 1);

IF ISNULL(value)
    RETURN(NaN);
END
 
value
Write color temperature
SENDHTTPREQUEST("light/0?transition=" + transitionTime + "&brightness=" + ROUND(Hb*100) + "&temp=" + Ct);

VAR now := NOW();
debounceTimestamp := now.Ticks + transitionTime + 500;
Electric Meter Electricity Meter Read-only

Power consumption and energy metering — instantaneous power (kW) and cumulative energy (kWh)

numeric Unit: W / kWh json_path

Electric Meter

Read total consumption
IF LENGTH(jsonStatus) = 0
    RETURN(NaN);
END

VAR power := PARSEJSON(jsonStatus, "$.meters[0].power");
VAR total := PARSEJSON(jsonStatus, "$.meters[0].total");
BOOL isOn := PARSEJSON(jsonStatus, "$.lights[0].ison");

IF ISNULL(power) OR ISNULL(total) OR (ison AND power=0)
	ADDWARNING("To enable power monitoring, open device settings in Shelly app or web browser and configure device model in Settings->Device Model");
	
    RETURN(NaN);
END

total / 60000.0
Read demand
IF LENGTH(jsonStatus) = 0
    RETURN(NaN);
END

VAR power := PARSEJSON(jsonStatus, "$.meters[0].power", 1);

IF ISNULL(power)
    RETURN(NaN);
END

power / 1000.0
Connection: Packet Parser → HTTP
Possible improvements (7)
  • Light Timer Active — Boolean — whether a countdown timer is currently active on the light output.
  • Timer Remaining — Seconds remaining on active timer. Available in /status response.
  • Auto-off Timer — Auto-off timer in seconds, could be added as service action or light parameter.
  • WiFi Signal Strength (as device) — WiFi RSSI in dBm. HTTP template exposes it as service attribute but not as a standalone sensor device.
  • Internal Device Temperature — Internal temperature in celsius. Available in /status for Gen1 devices. Not exposed in DUO template.
  • MQTT Real-time Power (as device attribute) — MQTT template reads power in electric meter device but does not expose it as a service attribute on the light device.
  • Connection Status — LWT topic — true on connect, false on disconnect. Could detect offline bulbs.

Sources