Skip to main content
Version: 1.0

Local Network Messaging (LNM) — Setup Guide

Local Network Messaging (LNM) lets Shelly devices exchange data directly over the local network using UDP multicast, with no cloud and no internet connection required. A device can transmit the status of its components, send a command to a group of devices at once, and react to messages from other devices on the same network.

LNM is a dynamic component: instances are created at runtime and do not exist by default. Each device supports up to 5 instances (IDs 200299, keys lnm:200, lnm:201, …). Each instance is one independent group, with its own multicast address, transmit list, and settings.

This guide covers setting up communication between two devices, configuring TX (Transmit) / RX (Receive) / RPC, sending group commands with LNM.Call, building automations in the web interface, and using LNM in scripts. You can configure LNM either from the device's web interface, in its Local Network Messaging section, or over RPC. The RPC examples below are shown as HTTP GET requests (with curl and mos alternatives); they use 192.168.33.1, the device's address in AP mode — replace it with your device's IP on your network.

For the complete API reference — all RPC methods, the configuration object, and the binary wire format — see the LNM component reference.

caution

LNM is available as a preview. The API may change in future firmware.

info

The complete LNM functionality — sending component status, group commands (LNM.Call), and webhook triggers — is available as of firmware 2.0.0-beta3. See the API docs changelog for the full list of changes in this release.

Multicast address and port

A group is identified by a multicast address and a port, written together, for example 239.255.0.1:3333. Two devices communicate only when both use the same address and the same port.

A multicast address identifies a group, not a single device, so its numbers do not map to a network and a host the way a normal IP does. Only the first number carries fixed meaning: a value of 224–239 marks the address as multicast. The remaining numbers are the group identifier you choose, and the port separates applications that share the same group. In practice you do not interpret the individual numbers — you pick one address from the recommended range and use the same address and port on every device in the group.

Use an address in the 239.255.x.y range. A reliable default is:

239.255.0.1:3333

If you run several LNM instances, give each its own address. For example:

239.255.0.1:3333
239.255.0.2:3333
239.255.0.3:3333
239.255.0.4:3333
239.255.0.5:3333

Port: 3333 is a suitable default. Any port from 102465535 works, provided it is identical on every device in the group.

Why this range, and what to avoid

The full multicast space (224.0.0.0239.255.255.255) is split by IANA (the Internet Assigned Numbers Authority) into blocks, most of which are reserved for networking protocols and internet-wide use. Only the 239.0.0.0/8 block is set aside for private use inside your own network — it is the multicast counterpart of the 192.168.x.x addresses on your LAN, and consumer routers handle it predictably.

Within 239.0.0.0/8, the 239.255.0.0/16 range is the site-local scope — the smallest scope, meant never to leave a single network, and the safest choice for a home or office LAN. Any 239.255.x.y address belongs to it (the 255 in the second position marks this scope). The wider 239.192.x.x239.251.x.x range is also private (the organization-local scope) and works too, for addresses outside the site-local range.

The remaining ranges should be avoided, because they either collide with low-level hardware addressing or are already used by other services:

RangeUse for LNM?Reason
239.255.x.yRecommendedSite-local; the smallest private scope, predictable on home networks
239.192.x.x239.251.x.xAlso worksOrganization-local private range — use only if you need addresses outside the site-local range
239.0.0.x, 239.128.0.xAvoidCollide with the low-level hardware addresses of the control range; many switches flood or mishandle them
239.255.255.xAvoidReserved for other services (e.g. SSDP/UPnP)
224.0.0.xAvoidReserved for network-control protocols; not forwarded off the local segment
225.x238.xAvoidReserved or special-purpose

References: IANA IPv4 Multicast registry, RFC 2365.

Set up and confirm communication

The goal here is a single confirmed result: one device transmits, another receives, and the received-message counter increases. Complete this before configuring anything else.

1. Create an instance on both devices, using the same address.

  • Web interface: open the Local Network Messaging section, add an instance, enter the multicast address (239.255.0.1:3333), and save.
  • RPC:
http://192.168.33.1/rpc/LNM.Create?config={"addr":"239.255.0.1:3333"}

Response

{
"id": 200
}

2. On the sending device, enable TX and select a component to send (here, a relay's switch:0).

  • Web interface: open the instance, enable TX (Transmit), then choose the component(s) to send.
  • RPC:
http://192.168.33.1/rpc/LNM.SetConfig?id=200&config={"tx":{"enable":true,"components":["switch:0"]}}

3. On the receiving device, enable RX.

  • Web interface: open the instance and enable RX (Receive).
  • RPC:
http://192.168.33.1/rpc/LNM.SetConfig?id=200&config={"rx":{"enable":true}}

4. Confirm delivery.

  • Web interface: the receiving device's instance shows an RX message counter that increases.
  • RPC: read the statistics twice, a few seconds apart:
http://192.168.33.1/rpc/LNM.GetStatus?id=200

Response

{
"id": 200,
"stats": {
"tx_msgs": 0,
"rx_msgs": 42,
"since": 1774350141
}
}

If rx_msgs increases, the two devices can hear each other and the group is working. If it stays at 0, see Troubleshooting.

note
  • All devices in a group must be on the same network segment (same Wi-Fi/LAN). Guest and IoT networks are usually isolated from each other.
  • A device does not receive its own messages — testing always requires at least two devices.
  • Changing the address later requires a device restart (LNM.SetConfig returns restart_required: true). All other settings apply immediately.

TX, RX, and RPC

Each instance has three independent settings, shown as Enable TX, Enable RX, and Enable RPC in the web interface. Enable any combination per device.

TX (Transmit)

Sends the status of the components you select to the group. Enable it on a device whose state other devices need to observe or react to.

  • Example: a Shelly Pro 3EM sends grid power to the group each measurement cycle; an EV charger on the same group reads it and reduces its charging rate when total load is high.
  • After enabling TX, select the components to send. Include only what you need — sending fewer components keeps network load low.

Components that can be sent:

ComponentSent asSent when
SwitchstatusOutput on/off change (plus periodic updates on metered models)
Light / RGB / RGBW / RGBCCT / CCTstatusOn/off, brightness, or colour change
CoverstatusOpen / close / stop / position change
Input (switch mode)statusState change
Input (button mode)eventButton action: single_push, double_push, triple_push, long_push
PM1 / EM / EM1statusEach measurement cycle

RX (Receive)

Joins the group and processes incoming messages. Required for a device's Actions (webhooks) or scripts to react to anything on the group.

  • Example: a light enables RX so it can act on a button device's presses.

RPC

Enables the LNM.Call mechanism, described in the next section. A device needs RPC enabled to send an LNM.Call and to execute one it receives.

  • Example: a Shelly i4 switches every light in the group off with one press.

Which to enable:

GoalSenderReceiver
Mirror one device's state onto anotherTXRX
One device commands a groupRPCRPC
Trigger an Action (webhook) from a received messageTXRX
React in a script to a received messageTXRX

Sending commands to a group with LNM.Call

LNM.Call sends a standard RPC method to the whole group. You provide the name of any Shelly RPC method — Switch.Set, RGBCCT.Set, Cover.Open, and so on — together with the parameters that method takes. Every device on the group that has RPC enabled runs the method on itself, exactly as if the call had been made directly on each device. Anything you can do to a single device over its local API can therefore be done to a whole group at once.

1. Enable RPC on the sender and on each receiver (Enable RPC in the web interface, or):

http://192.168.33.1/rpc/LNM.SetConfig?id=200&config={"rpc_enable":true}

2. Send a command. The parameters are id (the LNM instance), method, and params:

http://192.168.33.1/rpc/LNM.Call?id=200&method="Switch.Set"&params={"id":0,"on":true}

Response

{
"id": 200
}

Delivery is fire-and-forget — receivers' replies are not returned. Further examples:

Goalmethodparams
Turn all relays offSwitch.Set{"id":0,"on":false}
Set a colourRGBCCT.Set{"id":0,"on":true,"rgb":[255,0,0],"brightness":100}
Open a coverCover.Open{"id":0}
caution

RPC must be enabled on both ends: on the sender to be allowed to send (otherwise the call returns error -109, "RPC is not enabled on this instance", and nothing is sent), and on each receiver to execute the command. tx.enable is not required for LNM.Call. Receivers execute the command but never resend it, so there is no risk of a message loop.

For the full LNM.Call definition, see the LNM component reference.

Reacting with Actions (webhooks)

A device with RX enabled can run an Action when it receives a message, configured entirely in the web interface — no scripting needed. When you create a new Action on the receiving device, select the LNM instance (for example LNM (200)) as its component; the two LNM events below then become available as triggers. Each Action has a trigger event, an optional condition that filters it, and one or more actions to run.

LNM eventFires whenAvailable data
LNM status received (lnm.rx_status)a status message is receivedev.device, ev.status
LNM event received (lnm.rx_event)an event (e.g. a button press) is received — one per eventev.device, ev.event_data

Filtering with a condition

Add a condition to run the Action only in specific cases. A condition is a short expression:

Run only when…Condition
…a specific device sent the messageev.device === "shellyi4g3-3030f9ecc8f8"
…a received relay is onev.status["switch:0"].output === true
…a long button press was receivedev.event_data.event === "long_push"

The main button gestures to filter on are single_push, double_push, triple_push, and long_push. The stream can also include the lower-level btn_down and btn_up events (the physical press and release).

Choosing what the Action does

There are two ways to define what the Action does:

  • A guided action — for LNM, choose LNM Set Output or LNM Toggle Output (pick the component type — Switch, Light, CCT, RGB, RGBW, RGBCCT — the Component ID, and On/Off). This builds an LNM.Call for you and is the simplest way to start.
  • A custom URL — call any endpoint directly. The editor offers quick-insert buttons for the values available on the event: ${ev.device} and ${ev.event_data} for LNM event received, or ${ev.device} and ${ev.status} for LNM status received. Extend these with the specific field you need, for example ${ev.status["switch:0"].output}.

The custom-URL examples below use localhost — the device's own loopback address (127.0.0.1), i.e. the device calling itself — so you can test them without any other equipment.

Mirror a received relay state onto this device's relay

  • Trigger: LNM status received
http://localhost/rpc/Switch.Set?id=0&on=${ev.status["switch:0"].output}

Toggle this device's relay on a long press

  • Trigger: LNM event received
  • Condition: ev.event_data.event === "long_push"
http://localhost/rpc/Switch.Toggle?id=0

Send a group command in response (via LNM.Call)

http://localhost/rpc/LNM.Call?id=200&method=Switch.Set&params={"id":0,"on":false}
note

Actions fire only while RX is enabled, and deleting an LNM instance removes the Actions attached to it. The guided LNM actions currently cover common cases; the web interface for LNM actions will be expanded in future firmware, and the custom-URL option is available for anything not yet covered.

See the Webhook reference for the full Action/webhook API, and the LNM component reference for the event attributes.

Using LNM in scripts

On a device with RX enabled, a script receives an rx event for each incoming message:

{
"name": "lnm",
"info": {
"event": "rx",
"device": "shellyi4g3-3030f9ecc8f8",
"status": { "switch:0": { "output": true } },
"events": [ { "component": "input:0", "id": 0, "event": "single_push" } ]
}
}
  • ev.info.device — the sender's device ID
  • ev.info.status — received component status (present for status messages)
  • ev.info.events — received events such as button presses (present for event messages)
caution

The rx event can fire several times per second, and the stream includes lower-level events (btn_down, btn_up). Filter for the exact event you need, and keep handlers short — avoid issuing many calls per event.

The four examples below are independent — each is a complete script for a single device. For the script event details, see the LNM component reference and the Scripts documentation.

Button remote — runs on the sending device. A Shelly i4 whose four buttons control a Shelly Multicolor Bulb Gen3 on the group. Requires RPC enabled on this instance.

let LNM_ID = 200;   // this device's LNM instance id
let TARGET = 0; // rgbcct id on the receiving bulb (a Shelly Multicolor Bulb Gen3)
function lnm(method, params) {
Shelly.call("LNM.Call", { id: LNM_ID, method: method, params: params },
function (res, ec, em) { if (ec !== 0) print("LNM.Call error:", em); });
}
function onButton(inputId, push) {
if (push !== "single_push" && push !== "long_push") return; // ignore lower-level events
if (inputId === 0) lnm("RGBCCT.Set", { id: TARGET, on: true, brightness: 100 });
else if (inputId === 1) lnm("RGBCCT.Set", { id: TARGET, on: false });
else if (inputId === 2) lnm("RGBCCT.Set", { id: TARGET, on: true, brightness: 100, rgb: [255, 0, 0] });
else if (inputId === 3) lnm("RGBCCT.Toggle", { id: TARGET });
}
Shelly.addEventHandler(function (ev) {
if (!ev.component || ev.component.indexOf("input:") !== 0) return;
if (!ev.info || !ev.info.event) return;
onButton(Number(ev.component.split(":")[1]), ev.info.event);
});

Good-night routine — runs on the sending device. Sends a group command to switch every relay and light off. Trigger it however you like — from a Schedule at a fixed time, a button, or (shown here) a timer.

let LNM_ID = 200;   // this device's LNM instance id
function goodNight() {
Shelly.call("LNM.Call", { id: LNM_ID, method: "Switch.Set", params: { id: 0, on: false } });
Shelly.call("LNM.Call", { id: LNM_ID, method: "RGBCCT.Set", params: { id: 0, on: false } });
}
// Example trigger: run once every 24 hours. In practice, call goodNight() from a
// Schedule at a fixed time (e.g. 23:00), or from a physical button on this device.
Timer.set(24 * 60 * 60 * 1000, true, goodNight);

React to a received event (a button press) — runs on the receiving device. Turns button presses received from a sender into local light actions. Requires RX enabled here, and the sending device transmitting its inputs.

let TARGET = 0;   // local rgbcct id
Shelly.addEventHandler(function (ev) {
if (ev.name !== "lnm" || !ev.info || ev.info.event !== "rx") return;
if (!ev.info.events) return;
let evs = ev.info.events;
for (let i = 0; i < evs.length; i++) {
let e = evs[i];
if (!e.component || e.component.indexOf("input:") !== 0) continue;
if (e.event !== "single_push" && e.event !== "long_push") continue;
let id = Number(e.component.split(":")[1]);
if (id === 0) Shelly.call("RGBCCT.Set", { id: TARGET, on: true, brightness: 100 });
else if (id === 1) Shelly.call("RGBCCT.Set", { id: TARGET, on: false });
else if (id === 2) Shelly.call("RGBCCT.Set", { id: TARGET, on: true, brightness: 100, rgb: [0, 0, 255] });
else if (id === 3) Shelly.call("RGBCCT.Toggle", { id: TARGET });
}
});

React to a received status (mirror a device's state) — runs on the receiving device. Keeps this device's output in sync with a sender's status. Requires RX enabled.

let SENDER_ID  = "";           // "" = mirror any sender; or set one device id to lock to it
let SOURCE_KEY = "rgbcct:0"; // component to mirror (use "switch:0" for a relay)
let TARGET = 0; // local rgbcct id
Shelly.addEventHandler(function (ev) {
if (ev.name !== "lnm" || !ev.info || ev.info.event !== "rx") return;
if (SENDER_ID && ev.info.device !== SENDER_ID) return;
let st = ev.info.status;
if (!st || !st[SOURCE_KEY]) return;
if (typeof st[SOURCE_KEY].output === "boolean") {
Shelly.call("RGBCCT.Set", { id: TARGET, on: st[SOURCE_KEY].output });
}
});

Troubleshooting

If rx_msgs does not increase, the cause is almost always the network, not LNM:

CheckAction
Same address and portConfirm with LNM.GetConfig?id=200 on each device.
Recommended rangeUse 239.255.x.y; move off 224.0.0.x and other reserved ranges.
Same network segmentAll devices on one Wi-Fi/LAN; guest and IoT networks are isolated.
AP / Client IsolationThis router setting blocks device-to-device traffic; disable it for the devices' network.
IGMP snooping / querierNetworks use IGMP to manage which ports receive multicast. With IGMP snooping enabled but no active querier, a group's membership can age out and delivery may stop after a while. If the router exposes these settings, pair snooping with an IGMP querier (or proxy); if it still drops traffic, disabling snooping can help. A router restart restores delivery temporarily.
Mesh Wi-FiMany mesh systems expose no multicast setting; keep sender and receiver on the same network name and use a 239.255.x.y address.

Because delivery is fire-and-forget, occasional message loss over UDP multicast is normal and is not a fault.

Reference