Skip to content

LAN access

Controllers on firmware 2.5.46 and later serve Command Protocol v2 on the local network. Reads need no credential beyond being on the same network; writes need the controller’s envelope key. Place controllers on a trusted network segment.

The controller announces itself with mDNS:

Item Value
Service type _splashme._tcp
Port 8080
Hostname splashme-<last three MAC octets, lower-case hex>.local, for example splashme-000001.local for MAC 02:53:4D:00:00:01
TXT id Controller identifier, for example 02_53_4D_00_00_01
TXT mac 12 lower-case hex characters
TXT fw Firmware version
TXT pv 2

Key your records on the mac TXT record: the IP address can change. The hostname can also be resolved with a unicast .local lookup where multicast is blocked. A controller on both Wi-Fi and Ethernet announces one identity.

POST http://<ip>:8080/sm/2/lan/pv2
Content-Type: application/json
Content-Length: <1 to 1024>
{ "op": "state" | "a" | "q", "name": "<action or query name>", "env": "<hex of the signed envelope>" }

Content-Length is required. name and env may be empty for op: "state". Whitespace in the hex is ignored.

The controller handles one request at a time and closes the connection after each response. It does not accept connections during a firmware update, before it has an IP address, or for a moment while its cloud TLS handshake is in flight; treat a connect timeout as transient and retry. A 10-second client timeout and a poll interval of 15 seconds work well.

All application-level results come back as HTTP 200 with a JSON body.

op Signature Success Failure
state Not required { "env": "<hex signed full state frame>" } { "ok": false }
a Required, strict counter { "ok": true, "env": "<hex signed full state frame>" }, taken after the action ran; env is omitted if the frame could not be built { "ok": false }
q Required, strict counter { "env": "<hex signed query reply>" } { "ok": false }

{"ok": false} covers a bad tag, a replayed counter, an unknown or refused name, and an action the controller declined. Other bodies: {"ok": false, "err": "bad json"}, {"ok": false, "err": "bad op"}, and {"ok": false, "err": "busy"} when the controller could not free its frame buffer within 2 seconds.

Every env in a response is signed with the controller’s key and its own counter; verify it. The env you send for a and q is byte for byte what you would publish over MQTT, so encoders are shared between transports.

Status Body Cause
403 {"ok":false,"err":"not served on LAN"} A path other than /sm/2/lan/pv2, such as a v1 command name
405 {"ok":false,"err":"POST only"}
400 {"ok":false,"err":"invalid Content-Length"} Missing, zero or above 1024
400 {"ok":false,"err":"short body"} Body shorter than Content-Length
408 {"ok":false,"err":"slow headers"} Headers took more than 3 seconds

Even with the key, the following are refused on the LAN and return {"ok": false}.

  • Commands reserved for SplashMe installation and service tooling, whether sent through the cmd pass-through, the matching pass-through queries or reserved actions. If an integration needs one of them, contact support@splashmepool.com.au.

Everything else, including aux, schedule, set_time, evt_rewind, the native queries and equipment, chemistry, heating, lighting and schedule pass-throughs, behaves as it does through the cloud. Events raised by LAN requests carry transport code 8, v2-lan.

Read the state without a key:

Terminal window
curl -s http://splashme-000001.local:8080/sm/2/lan/pv2 \
-H 'Content-Type: application/json' \
-d '{"op":"state","name":"","env":""}'

Verify the returned env with unwrap from Envelope and keys. The payload is a state frame; decoding it requires the wire specification.

Switch slot 5 on with the key:

import time, requests
env = wrap(key, int(time.time()), bytes([5, 1, 1])) # aux: slot 5, on, trump
r = requests.post("http://splashme-000001.local:8080/sm/2/lan/pv2",
json={"op": "a", "name": "aux", "env": env.hex()}, timeout=10)
body = r.json()
assert body.get("ok"), body
counter, frame = unwrap(key, bytes.fromhex(body["env"])) # fresh full state

Call GET …/envelope-key once with an OAuth token for an account that owns the controller, then store the key with your controller record. See Bring your own authentication for the recommended shape of a LAN-first integration.