TapHome

Shelly Motion 2

Packet Parser → MQTT
Submitted by
Last updated: 03. 2026
Shelly Motion 2

The Shelly Motion 2 (SHMOS-02) is a battery-powered (Li-ion 6500 mAh, USB-C rechargeable) Wi-Fi PIR motion sensor. TapHome communicates with the device over MQTT — the recommended protocol for battery-operated Shelly devices, since the sensor sleeps most of the time and publishes data only on wake-up events (motion detection or periodic timer).

Unlike relay-type Gen1 Shelly devices that publish individual state topics, the Motion 2 publishes its complete status via a single /info JSON payload when it wakes. The TapHome listener script parses this JSON to extract motion state, luminance, temperature, battery level and diagnostic attributes.

The template supports up to 5 Shelly Motion 2 sensors per module. Each sensor instance is distinguished by a sensorN_topic custom variable pointing to the device’s MQTT Client ID.

Configuration

Device ID

Each Shelly Motion 2 has a unique MQTT Device ID in the format shellymotion2-<MAC>, where <MAC> is the full 12-character MAC address in uppercase hex (e.g., shellymotion2-AABBCCDDEEFF).

The Device ID can be found:

  • On the device label (MAC address)
  • In the Shelly web UI: SettingsDevice Info
  • Via API: GET http://<device-ip>/settingsdevice.hostname field
Template setup

After importing the template in TapHome:

  1. Open the Shelly Motion 2 MQTT module
  2. Set the MQTT Broker IP and Port (default 1883)
  3. For each sensor instance, set the sensorN_topic custom variable to the Device ID of the corresponding Shelly Motion 2 (e.g., shellymotion2-AABBCCDDEEFF)

The module subscribes to shellies/# and the listener scripts filter messages by the configured topic prefix.

Only configure the sensor slots you actually use. Unconfigured slots (with the default shellymotion2-deviceid value) will show an error message prompting you to set the correct topic.

Motion sensor settings

The Shelly Motion 2 has several configurable parameters that affect detection behavior. These are configured directly on the device via its web UI (http://<device-ip>/), not through TapHome:

ParameterRangeDescription
Sensitivity1–256Motion detection sensitivity (lower = more sensitive)
Blind time1–1440 minMinutes to ignore motion after a detection event
Pulse count1–4Consecutive movements required to confirm motion
Operating modeANY / DARK / TWILIGHT / BRIGHTWhen detection is active based on ambient light
Sleep time0–86400 sPeriodic wake interval in seconds

Shorter sleep times increase data freshness but reduce battery life. The default wake interval is sufficient for most use cases. Motion events always trigger an immediate wake regardless of the sleep timer.

Device capabilities

Motion detection

Each sensor instance is mapped as a Reed Contact device with “security” capability in TapHome. The motion state is parsed from the shellies/<id>/info topic — specifically the $.sensor.motion field in the JSON payload:

  • trueAlarm (motion detected)
  • falseOK (no motion)

The sensor wakes up and publishes its state when motion is detected (subject to sensitivity, blind time and pulse count settings), and also during periodic wake intervals.

Service attributes

Each sensor instance exposes 14 device-level service attributes, all parsed from the /info JSON payload:

AttributeSource fieldDescription
IP Address$.wifi_sta.ipWi-Fi IP address of the sensor
MAC Address$.macHardware MAC address
Battery$.bat.voltage, $.bat.valueBattery voltage and percentage (e.g., 3.83V (97%))
Signal$.wifi_sta.rssiWi-Fi signal strength in dB
Cloud enabled$.cloud.enabledWhether Shelly Cloud is enabled
Cloud connected$.cloud.connectedWhether device is connected to Shelly Cloud
Device time$.timeCurrent time on the device
Luminance$.lux.valueAmbient light level in lux
Temperature$.tmp.value, $.tmp.unitsInternal temperature reading
Uptime$.uptimeTime since last reboot
FW version$.fw_info.fwCurrent firmware version
FW update$.update.has_updateWhether a firmware update is available
Free RAM$.ram_total, $.ram_freeAvailable memory
Free FS space$.fs_size, $.fs_freeAvailable filesystem storage

A low battery warning is triggered automatically when the battery level drops below 20%.

Additional capabilities (not implemented)

The Shelly Motion 2 also publishes vibration/tamper detection ($.sensor.vibration, configurable sensitivity 0–80), illumination category ($.lux.illumination — dark/twilight/bright), and USB charger status ($.charger) via the /info JSON payload. An online/offline status is available via the LWT topic (shellies/<id>/online). These capabilities are available in the device’s MQTT output but are not mapped in the current TapHome template. They can be added in a future template update.

Troubleshooting

Sensor not reporting data
  1. Verify the Shelly Motion 2 is connected to Wi-Fi and MQTT is enabled in the device settings
  2. Check that the sensorN_topic custom variable matches the Device ID exactly (e.g., shellymotion2-AABBCCDDEEFF)
  3. Use an MQTT client (e.g., MQTT Explorer) to subscribe to shellies/# and verify the sensor publishes messages on wake-up
  4. The Motion 2 is battery-operated and only publishes data when it wakes — trigger a motion event to force a wake-up
Motion state not updating
  1. Check that the PIR sensor lens is not obstructed and the sensor is mounted at the recommended height (1.8–2.5 m)
  2. If the sensor was recently installed, verify the sensitivity and blind time settings in the device web UI — a high blind time means the sensor ignores motion for longer periods after each detection
  3. Adjust the pulse count if the sensor is triggering too rarely (lower = fewer consecutive movements needed)
  4. Check TapHome for the Reed Contact device state — 1 = motion detected (alarm), 0 = no motion (OK)
Battery draining quickly
  1. Shelly Motion 2 uses a built-in Li-ion rechargeable battery (6500 mAh) charged via USB-C, with typical active life of 12–18 months
  2. Frequent wake-ups due to high motion activity or short sleep intervals drain the battery faster
  3. Enabling SSL on MQTT significantly reduces battery life — use plain MQTT (port 1883) on a trusted local network
  4. Ensure strong Wi-Fi signal at the sensor location — weak signal means longer connection times on each wake

Gen1 Shelly devices do not support MQTT over TLS without significant battery impact. Communication between the sensor and the MQTT broker is typically unencrypted (plain MQTT, port 1883). Ensure the MQTT broker is on a trusted local network.

Enabling MQTT on Gen1 Shelly devices disables Cloud connectivity. MQTT and Cloud cannot coexist on the same device.

How to install in TapHome

Prerequisites

  • Shelly device connected to Wi-Fi (see HTTP connection guide if not done yet)
  • MQTT broker running on your local network (e.g., Mosquitto, Home Assistant, or TapHome built-in broker)
  • TapHome CCU on the same network as the broker

On Gen1 devices, enabling MQTT disables Shelly Cloud. Both cannot run simultaneously. On Gen2/Plus devices this limitation does not apply.

Step 1 — Enable MQTT on the Shelly device

Gen1 devices (Shelly 1, 1PM, 2.5, EM, 3EM, Plug S, RGBW2, Dimmer, TRV…)

  1. Open the Shelly web UI: http://<device-ip>/
  2. Navigate to Internet & SecurityAdvanced — MQTT
  3. Enable MQTT
  4. Set MQTT Server: <broker-ip>:<port> (e.g., 192.168.1.10:1883)
  5. Optionally set MQTT User and MQTT Password if your broker requires authentication
  6. Click Save — the device will reboot and connect to the broker

Gen2 / Plus devices (Shelly Plus 1, Plus 1PM, Plus 2PM, Plus Plug S, Plus H&T, Pro 3EM…)

  1. Open the Shelly web UI: http://<device-ip>/
  2. Navigate to SettingsMQTT
  3. Enable MQTT
  4. Set Server: <broker-ip>:<port> (e.g., 192.168.1.10:1883)
  5. The Client ID is pre-filled with the device ID (e.g., shellyplus1pm-AABBCCDDEE) — leave as-is unless you have a specific reason to change it
  6. Click Save and reboot the device

To verify MQTT is working, use an MQTT client (e.g., MQTT Explorer) and subscribe to shellies/# (Gen1) or <device-id>/# (Gen2). You should see status messages from the device.

Step 2 — Find the Device ID / MQTT Client ID

Some templates require a Device ID or MQTT Client ID parameter. This is the unique identifier used in MQTT topics.

  • Gen1: found on the label as MAC address (e.g., AABBCCDDEE). Device ID = shelly<model>-<mac>, e.g., shelly1pm-AABBCCDDEE
  • Gen2/Plus: found in the Shelly web UI under SettingsDevice InfoDevice ID, or on the device label

Step 3 — Configure in TapHome

  1. In TapHome, add a new Packet Parser (MQTT) module
  2. IP Address: enter the MQTT broker IP (e.g., 192.168.1.10)
  3. Port: 1883 (default; use 8883 for TLS)
  4. Device ID / MQTT Client ID: enter the value from Step 2 (if required by the template)
  5. Import the template — TapHome will subscribe to the device topics automatically

Available devices

Shelly Motion 2 MQTT Module
Custom Variables
sensor1_topic (string) = shellymotion2-deviceidMQTT device ID of motion sensor 1 — format is 'shellymotion2-DEVICEID' (find Device ID in Shelly web UI → Settings → Device Info)
Open http://shellyIpAddress → Settings → Device info → copy Device ID. Format: shellymotion2-<last6MAC>
sensor2_topic (string) = shellymotion2-deviceidMQTT device ID of motion sensor 2
sensor3_topic (string) = shellymotion2-deviceidMQTT device ID of motion sensor 3
sensor4_topic (string) = shellymotion2-deviceidMQTT device ID of motion sensor 4
sensor5_topic (string) = shellymotion2-deviceidMQTT device ID of motion sensor 5
Motion Detector 1 Reed Contact Read-only

PIR motion detection — reports alarm when motion detected, OK when no motion; parses $.sensor.motion from /info JSON payload

boolean
Service Attributes
IP Address
MAC Address
Battery
Signal
Cloud enabled
Cloud connected
Device time
Luminance
Temperature
Uptime
FW version
FW update
Free RAM
Free FS space

Motion Detector 1

Listener
IF (INDEXOF(sensor1_topic, "shellymotion2-deviceid") = 0)
    ADDERROR("Set correct 'sensor1_topic' value in module variables. Topic format is 'shellymotion2-<deviceid>'. The Device ID can be found by opening url 'http://shellyIpAddress', in Settings -> Device info.");
    RETURN(-1);
END

VAR topicPrefix := "shellies/" + sensor1_topic;

IF (INDEXOF(RECEIVEDMSG.TOPIC, topicPrefix + "/info") = 0)
    BOOL status := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.sensor.motion", 1);
    
    IF ISNULL(status)
        Rc := NaN;
    ELSE
        Rc := status;
    END
    
    VAR battery := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.bat.value");
    IF battery < 20
        ADDWARNING("Battery low (" + battery + "%)");
    END
END
Service Attributes
IP Address
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.ip"));
END
MAC Address
IF(LENGTH(infoJson) = 0, "-", PARSEJSON(infoJson, "$.mac"));

IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.mac"));
END
Battery
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR voltage := PARSEJSON(infoJson, "$.bat.voltage");
    VAR percentage := PARSEJSON(infoJson, "$.bat.value");

    RETURN(voltage + "V (" + percentage + "%)");
END
Signal
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.rssi", 1) + "db");
END
Cloud enabled
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.enabled"));
END
Cloud connected
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.connected"));
END
Device time
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.time"));
END
Luminance
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.lux.value", 1) + "lux");
END
Temperature
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.tmp.value", 1) + PARSEJSON(infoJson, "$.tmp.units", 1));
END
Uptime
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR upTimeSeconds := PARSEJSON(infoJson, "$.uptime", 1);
    VAR days := FLOOR(upTimeSeconds/86400, 1);
    uptimeSeconds := MOD(upTimeSeconds, 86400);
    var hours := FLOOR(upTimeSeconds/3600, 1);
    uptimeSeconds := MOD(upTimeSeconds, 3600);
    var minutes := FLOOR(upTimeSeconds/60, 1);
    RETURN(days + "day(s) " + hours + "h " + minutes + "m");
END
FW version
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.fw_info.fw"));
END
FW update
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.update.has_update", 1));
END
Free RAM
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.ram_total");
    VAR free := PARSEJSON(infoJson, "$.ram_free");
    RETURN(free + "/" + total + "(B)");
END
Free FS space
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.fs_size");
    VAR free := PARSEJSON(infoJson, "$.fs_free");
    RETURN(free + "/" + total + "(B)");
END
Motion Detector 2 Reed Contact Read-only

PIR motion detection — reports alarm when motion detected, OK when no motion; parses $.sensor.motion from /info JSON payload

boolean
Service Attributes
IP Address
MAC Address
Battery
Signal
Cloud enabled
Cloud connected
Device time
Luminance
Temperature
Uptime
FW version
FW update
Free RAM
Free FS space

Motion Detector 2

Listener
IF (INDEXOF(sensor2_topic, "shellymotion2-deviceid") = 0)
    ADDERROR("Set correct 'sensor2_topic' value in module variables. Topic format is 'shellymotion2-<deviceid>'. The Device ID can be found by opening url 'http://shellyIpAddress', in Settings -> Device info.");
    RETURN(-1);
END

VAR topicPrefix := "shellies/" + sensor2_topic;

IF (INDEXOF(RECEIVEDMSG.TOPIC, topicPrefix + "/info") = 0)
    BOOL status := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.sensor.motion", 1);
    
    IF ISNULL(status)
        Rc := NaN;
    ELSE
        Rc := status;
    END
    
    VAR battery := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.bat.value");
    IF battery < 20
        ADDWARNING("Battery low (" + battery + "%)");
    END
END
Service Attributes
IP Address
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.ip"));
END
MAC Address
IF(LENGTH(infoJson) = 0, "-", PARSEJSON(infoJson, "$.mac"));

IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.mac"));
END
Battery
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR voltage := PARSEJSON(infoJson, "$.bat.voltage");
    VAR percentage := PARSEJSON(infoJson, "$.bat.value");

    RETURN(voltage + "V (" + percentage + "%)");
END
Signal
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.rssi", 1) + "db");
END
Cloud enabled
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.enabled"));
END
Cloud connected
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.connected"));
END
Device time
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.time"));
END
Luminance
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.lux.value", 1) + "lux");
END
Temperature
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.tmp.value", 1) + PARSEJSON(infoJson, "$.tmp.units", 1));
END
Uptime
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR upTimeSeconds := PARSEJSON(infoJson, "$.uptime", 1);
    VAR days := FLOOR(upTimeSeconds/86400, 1);
    uptimeSeconds := MOD(upTimeSeconds, 86400);
    var hours := FLOOR(upTimeSeconds/3600, 1);
    uptimeSeconds := MOD(upTimeSeconds, 3600);
    var minutes := FLOOR(upTimeSeconds/60, 1);
    RETURN(days + "day(s) " + hours + "h " + minutes + "m");
END
FW version
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.fw_info.fw"));
END
FW update
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.update.has_update", 1));
END
Free RAM
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.ram_total");
    VAR free := PARSEJSON(infoJson, "$.ram_free");
    RETURN(free + "/" + total + "(B)");
END
Free FS space
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.fs_size");
    VAR free := PARSEJSON(infoJson, "$.fs_free");
    RETURN(free + "/" + total + "(B)");
END
Motion Detector 3 Reed Contact Read-only

PIR motion detection — reports alarm when motion detected, OK when no motion; parses $.sensor.motion from /info JSON payload

boolean
Service Attributes
IP Address
MAC Address
Battery
Signal
Cloud enabled
Cloud connected
Device time
Luminance
Temperature
Uptime
FW version
FW update
Free RAM
Free FS space

Motion Detector 3

Listener
IF (INDEXOF(sensor3_topic, "shellymotion2-deviceid") = 0)
    ADDERROR("Set correct 'sensor3_topic' value in module variables. Topic format is 'shellymotion2-<deviceid>'. The Device ID can be found by opening url 'http://shellyIpAddress', in Settings -> Device info.");
    RETURN(-1);
END

VAR topicPrefix := "shellies/" + sensor3_topic;

IF (INDEXOF(RECEIVEDMSG.TOPIC, topicPrefix + "/info") = 0)
    BOOL status := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.sensor.motion", 1);
    
    IF ISNULL(status)
        Rc := NaN;
    ELSE
        Rc := status;
    END
    
    VAR battery := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.bat.value");
    IF battery < 20
        ADDWARNING("Battery low (" + battery + "%)");
    END
END
Service Attributes
IP Address
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.ip"));
END
MAC Address
IF(LENGTH(infoJson) = 0, "-", PARSEJSON(infoJson, "$.mac"));

IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.mac"));
END
Battery
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR voltage := PARSEJSON(infoJson, "$.bat.voltage");
    VAR percentage := PARSEJSON(infoJson, "$.bat.value");

    RETURN(voltage + "V (" + percentage + "%)");
END
Signal
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.rssi", 1) + "db");
END
Cloud enabled
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.enabled"));
END
Cloud connected
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.connected"));
END
Device time
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.time"));
END
Luminance
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.lux.value", 1) + "lux");
END
Temperature
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.tmp.value", 1) + PARSEJSON(infoJson, "$.tmp.units", 1));
END
Uptime
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR upTimeSeconds := PARSEJSON(infoJson, "$.uptime", 1);
    VAR days := FLOOR(upTimeSeconds/86400, 1);
    uptimeSeconds := MOD(upTimeSeconds, 86400);
    var hours := FLOOR(upTimeSeconds/3600, 1);
    uptimeSeconds := MOD(upTimeSeconds, 3600);
    var minutes := FLOOR(upTimeSeconds/60, 1);
    RETURN(days + "day(s) " + hours + "h " + minutes + "m");
END
FW version
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.fw_info.fw"));
END
FW update
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.update.has_update", 1));
END
Free RAM
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.ram_total");
    VAR free := PARSEJSON(infoJson, "$.ram_free");
    RETURN(free + "/" + total + "(B)");
END
Free FS space
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.fs_size");
    VAR free := PARSEJSON(infoJson, "$.fs_free");
    RETURN(free + "/" + total + "(B)");
END
Motion Detector 4 Reed Contact Read-only

PIR motion detection — reports alarm when motion detected, OK when no motion; parses $.sensor.motion from /info JSON payload

boolean
Service Attributes
IP Address
MAC Address
Battery
Signal
Cloud enabled
Cloud connected
Device time
Luminance
Temperature
Uptime
FW version
FW update
Free RAM
Free FS space

Motion Detector 4

Listener
IF (INDEXOF(sensor4_topic, "shellymotion2-deviceid") = 0)
    ADDERROR("Set correct 'sensor4_topic' value in module variables. Topic format is 'shellymotion2-<deviceid>'. The Device ID can be found by opening url 'http://shellyIpAddress', in Settings -> Device info.");
    RETURN(-1);
END

VAR topicPrefix := "shellies/" + sensor4_topic;

IF (INDEXOF(RECEIVEDMSG.TOPIC, topicPrefix + "/info") = 0)
    BOOL status := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.sensor.motion", 1);
    
    IF ISNULL(status)
        Rc := NaN;
    ELSE
        Rc := status;
    END
    
    VAR battery := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.bat.value");
    IF battery < 20
        ADDWARNING("Battery low (" + battery + "%)");
    END
END
Service Attributes
IP Address
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.ip"));
END
MAC Address
IF(LENGTH(infoJson) = 0, "-", PARSEJSON(infoJson, "$.mac"));

IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.mac"));
END
Battery
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR voltage := PARSEJSON(infoJson, "$.bat.voltage");
    VAR percentage := PARSEJSON(infoJson, "$.bat.value");

    RETURN(voltage + "V (" + percentage + "%)");
END
Signal
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.rssi", 1) + "db");
END
Cloud enabled
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.enabled"));
END
Cloud connected
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.connected"));
END
Device time
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.time"));
END
Luminance
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.lux.value", 1) + "lux");
END
Temperature
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.tmp.value", 1) + PARSEJSON(infoJson, "$.tmp.units", 1));
END
Uptime
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR upTimeSeconds := PARSEJSON(infoJson, "$.uptime", 1);
    VAR days := FLOOR(upTimeSeconds/86400, 1);
    uptimeSeconds := MOD(upTimeSeconds, 86400);
    var hours := FLOOR(upTimeSeconds/3600, 1);
    uptimeSeconds := MOD(upTimeSeconds, 3600);
    var minutes := FLOOR(upTimeSeconds/60, 1);
    RETURN(days + "day(s) " + hours + "h " + minutes + "m");
END
FW version
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.fw_info.fw"));
END
FW update
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.update.has_update", 1));
END
Free RAM
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.ram_total");
    VAR free := PARSEJSON(infoJson, "$.ram_free");
    RETURN(free + "/" + total + "(B)");
END
Free FS space
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.fs_size");
    VAR free := PARSEJSON(infoJson, "$.fs_free");
    RETURN(free + "/" + total + "(B)");
END
Motion Detector 5 Reed Contact Read-only

PIR motion detection — reports alarm when motion detected, OK when no motion; parses $.sensor.motion from /info JSON payload

boolean
Service Attributes
IP Address
MAC Address
Battery
Signal
Cloud enabled
Cloud connected
Device time
Luminance
Temperature
Uptime
FW version
FW update
Free RAM
Free FS space

Motion Detector 5

Listener
IF (INDEXOF(sensor5_topic, "shellymotion2-deviceid") = 0)
    ADDERROR("Set correct 'sensor5_topic' value in module variables. Topic format is 'shellymotion2-<deviceid>'. The Device ID can be found by opening url 'http://shellyIpAddress', in Settings -> Device info.");
    RETURN(-1);
END

VAR topicPrefix := "shellies/" + sensor5_topic;

IF (INDEXOF(RECEIVEDMSG.TOPIC, topicPrefix + "/info") = 0)
    BOOL status := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.sensor.motion", 1);
    
    IF ISNULL(status)
        Rc := NaN;
    ELSE
        Rc := status;
    END
    
    VAR battery := PARSEJSON(RECEIVEDMSG.PAYLOAD, "$.bat.value");
    IF battery < 20
        ADDWARNING("Battery low (" + battery + "%)");
    END
END
Service Attributes
IP Address
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.ip"));
END
MAC Address
IF(LENGTH(infoJson) = 0, "-", PARSEJSON(infoJson, "$.mac"));

IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.mac"));
END
Battery
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR voltage := PARSEJSON(infoJson, "$.bat.voltage");
    VAR percentage := PARSEJSON(infoJson, "$.bat.value");

    RETURN(voltage + "V (" + percentage + "%)");
END
Signal
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.wifi_sta.rssi", 1) + "db");
END
Cloud enabled
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.enabled"));
END
Cloud connected
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.cloud.connected"));
END
Device time
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.time"));
END
Luminance
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.lux.value", 1) + "lux");
END
Temperature
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.tmp.value", 1) + PARSEJSON(infoJson, "$.tmp.units", 1));
END
Uptime
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR upTimeSeconds := PARSEJSON(infoJson, "$.uptime", 1);
    VAR days := FLOOR(upTimeSeconds/86400, 1);
    uptimeSeconds := MOD(upTimeSeconds, 86400);
    var hours := FLOOR(upTimeSeconds/3600, 1);
    uptimeSeconds := MOD(upTimeSeconds, 3600);
    var minutes := FLOOR(upTimeSeconds/60, 1);
    RETURN(days + "day(s) " + hours + "h " + minutes + "m");
END
FW version
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.fw_info.fw"));
END
FW update
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    RETURN(PARSEJSON(infoJson, "$.update.has_update", 1));
END
Free RAM
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.ram_total");
    VAR free := PARSEJSON(infoJson, "$.ram_free");
    RETURN(free + "/" + total + "(B)");
END
Free FS space
IF LENGTH(infoJson) = 0
    RETURN("-");
ELSE
    VAR total := PARSEJSON(infoJson, "$.fs_size");
    VAR free := PARSEJSON(infoJson, "$.fs_free");
    RETURN(free + "/" + total + "(B)");
END
Connection: Packet Parser → MQTT
Possible improvements (4)
  • Vibration / Tamper Detection — Vibration/tamper detection (bool). Available in the /info JSON but not parsed by the listener script. Configurable sensitivity 0-80.
  • Illumination Category — Light category string — dark/twilight/bright. Available in /info but only numeric lux is parsed via service attributes.
  • USB Charger Connected — Whether USB-C charger is plugged in. Available in /info JSON.
  • Connection Status — LWT topic — true on connect, false on disconnect. Could detect offline sensors.

Sources