Getting Started Guide
Thanks for testing out the Smart Sense Liquid Detect device! We look forward to hearing from you about how it worked and what we can do to make it meet your needs even more effectively. This guide walks through setting up each of the three configurations — Standalone, Smart (USB), or Smart Remote (Bluetooth) — and some tips and tricks to help you with all of the configurations.
Downloading and running the SmartSense App
No matter which way you’ll be using the Smart Sense LD, you’ll wan to download and run the Smart Sense application. It allows you to customize how the device functions. (Note: only available for Windows OS)
Steps:
Download the SmartSense.zip file from the Exypnos Automation Google Drive. Depending on your Windows settings, the downloaded file might need to be unblocked before you unzip it:
Right-click the blocked ZIP file and select Properties.
Look at the bottom of the General tab for a Security section.
Check the box labeled Unblock (if available).
Click Apply, then OK.
Extract the files normally.
Unzip it to your preferred location.
Navigate to the executable in the folder and double-click it. You might see a Windows security popup about the application not being digitally signed and you’ll need to navigate through that window. It shouldn’t pop up again.
The application will start and the control dashboard will launch. At this point you can close the application and it will minimize to your tray and run in the background. With a very low memory footprint, the application can be run in the background without bogging down your computer.
If you right click on the Smart Sense icon in the tray, you can exit the app completely. You can also select to have the app start each time windows is started. This is highly recommended unless you’re running the devices in standalone mode.
Navigating the software:
The dashboard shows the COM port used by the unit, the MAC address of the unit, the monitoring state and the status of the liquid detection sensor. These are informational, not editable.
You can edit the name of the device, change the alarm mode, alarm output, and LED settings. Once you’ve made your changes, hit Apply to send them to the unit. All settings are stored in permanent memory, so they’ll persist even after disconnecting from the computer.
Note: the dashboard will only show units that are physically connected by USB or wirelessly via the Smart Sense HUB (see more information below).
Standalone setup
With the application running, connect the Smart Sense: LD to your computer using the enclosed USB cable. (Yes, the USB-C port on these beta units is deeply recessed—this will be fixed in the production units.)
Once connected, you will see the device pop up on the dashboard. Go ahead and try changing the LED color or brightness to ensure you have connectivity to the device.
Attach the sensor to the main unit. Line up the 3 pins, insert and twist clockwise to lock in place. You can now test the unit by placing a couple of fingers over the sensor. You should see the LEDs flash red, hear the alarm siren, or both, depending on the Alarm Output settings.
Disconnect the USB from the computer position the main unit near the bottle or carboy you would like to monitor.
Using the included velcro strap, mount the sensor on the outside of your bottle or carboy at the height you want to monitor. It’s recommended to have at least 1 inch of overlap at each end of the velcro to ensure durable attachment.
Power on the device by plugging it into the supplied USB power adapter.
That's it! For more information, skip down to the tips and tricks section.
Smart (USB) setup
With the application running, connect the Smart Sense: LD to your computer using the enclosed USB cable. (Yes, the USB-C port on these beta units is deeply recessed—this will be fixed in the production units.)
Once connected, you will see the device pop up on the dashboard. Go ahead and try changing the LED color or brightness to ensure you have connectivity to the device.
Attach the sensor to the main unit. Line up the 3 pins, insert and twist clockwise to lock in place. You can now test the unit by placing a couple of fingers over the sensor. You should see the LEDs flash red, hear the alarm siren, or both, depending on the Alarm Output settings. You will also see the status change on the dashboard.
You can minimize the application or shrink it to your tray by hitting the red X on the application window. It will continue to run in the background.
While running in the background, with the unit connected by USB, the device runs in Smart mode with full programmatic control via the API (see more below on the API).
You can attach additional devices via USB and they’ll show up in the dashboard with the others.
That’s it! For more information, skip down to the tips and tricks section.
Smart Remote (Bluetooth) setup
With the application running, connect the Smart Sense HUB to your computer using the enclosed, short USB cable.
Attach the sensor to the main unit. Line up the 3 pins, insert and twist clockwise to lock in place.
Power the main unit by attaching the long USB cable to the unit and the enclosed USB power adapter. The LED ring should indicate it has power. With the hub connected to the computer and the main unit powered (and within bluetooth range), the unit will now show up on the dashboard. If you have multiple units connected, they’ll all be represented on the dashboard.
You can now test the unit by placing a couple of fingers over the sensor. You should see the LEDs flash red, hear the alarm siren, or both, depending on the Alarm Output settings. You will also see the status change on the dashboard. Note: it can take a couple of seconds to sync between the HUB and the remote unit(s).
Once connected, you will see the device pop up on the dashboard. Go ahead and try changing the LED color or brightness to ensure you have connectivity to the device.
Sensors within Bluetooth range of the Bridge appear in the dashboard automatically, alongside any USB-connected sensors, and are configured the same way.
You can minimize the application or shrink it to your tray by hitting the red X on the application window. It will continue to run in the background.
While running in the background, with the unit connected via the Smart Sense HUB, the system runs in Smart Remote mode with full programmatic control via the API (see more below on the API).
That’s it! For more information, skip down to the tips and tricks section.
TIPS and tricks:
Using the dashboard
Each row in the table shows a sensor connected by USB or wirelessly connected via the Smart Sense H:
Sensor Name— Give each unit a unique name (e.g. “waste carboy” or “Reagent A”)
Port — how the sensor is connected (a COM port for USB, or "via bridge" for a Bluetooth sensor reached through the Bridge)
MAC Address — the sensor's unique hardware ID, so you always know which physical unit you're looking at
State — Monitoring or Alarm, updated live
Liquid Sensor — whether liquid is currently detected at the sensor
Alarm Mode — choose "Alarm when liquid detected" or "Alarm when liquid absent" depending on your application. For instance, if monitoring liquid waste, you likely would set it to “Alarm with liquid detected” and place the sensor near the top of the container.
Alarm Output— choose how you would like to be notified (visual and audio, visual only, or audio only)
LED Settings— the ring color and brightness to display while Monitoring
Click Apply after changing settings to push it to the device. Your settings are saved on the sensor itself, so they'll still be there the next time it powers on. For wireless sensors, it can take a couple of seconds for changes to sync.
Reading the ring
A slow, soft "breathing" glow means the device is Monitoring normally. A fast flashing red ring with a two-tone chime means Alarm — the liquid has crossed your configured threshold. The direction the glow travels around the ring also tells you the alarm mode: starting from the bottom and meeting at the top means "alarm when liquid detected"; the reverse means "alarm when liquid absent."
Mounting the sensor
Use the enclosed velcro strap to mount the sensor to bottle or carboy to be monitored. Start at one end, feeding 1-2 inches of velcro through the opening on one of the sensor’s “wings.” Next, wrap the circle around the container and loop the other end through the opposite wing. Pull tight and fold the velcro back on itself to secure the sensor. For permanent installations, you may want to add double sided tape in addition to the strap (recommended: VHB by 3M).
The sensor must be positioned at the appropriate height to be effective. For applications where liquid is accumulating (and you want to prevent overflow), position the sensor near the top of the container and set the unit to “alarm when liquid detected.” For applications where liquid is being consumed (and you want to prevent full depletion), position the sensor near the bottom of the container and set the unit to “alarm when liquid not detected.”
A note on liquids and containers
SmartSense LD has been validated with many liquids, including those containing water, alcohol, and detergents. Because it senses via the liquid's dielectric properties, it's expected to be less reliable with low-dielectric liquids such as oils or nonpolar solvents — we recommend a quick test with your specific liquid before relying on it for those. The sensor has been tested on and works well with plastic and glass containers, but it is not compatible with metal containers.
We welcome your feedback about liquids, containers, and applications that work with the Smart Sense LD. If it doesn’t work with your applications, we’ll happily refund your payment once it’s returned to us.
For developers: the local API
Every Smart Sense app instance exposes a local REST API at the same address (http://localhost:5177) the dashboard runs at — nothing to install separately, and nothing leaves your network. Use it to pull live device status into your own tools instead of watching the dashboard by hand.
Query any connected sensor's current state (Monitoring or Alarm), whether liquid is detected, its alarm mode, its indicator color, and its unique hardware ID
Poll on whatever interval suits your workflow — feed a logging system, a custom dashboard, an alerting pipeline, or a LIMS integration
Works the same for USB- and Bluetooth-connected sensors — the API doesn't distinguish how a device is connected
No cloud, no external accounts, no authentication to manage — the API is only reachable on your local machine/network, same as the dashboard
See the API reference for full endpoint details and response formats.
API Reference
Local REST API — for developers
Overview
The SmartSense host service exposes a local REST API at http://localhost:5177. The port is configurable via the PORT environment variable. The server binds to 127.0.0.1 only — by design it is not reachable from other machines on the network. All responses are JSON.
MAC addresses are the canonical device ID throughout, in the format AA:BB:CC:DD:EE:FF (uppercase, colon-separated). This is the :mac path parameter on every per-device endpoint.
Two things to plan for
No authentication. This is a loopback-bound local service with no auth layer — anything that can reach localhost on the machine can read and reconfigure every connected device.
No push channel. There is no WebSocket or server-sent-events endpoint; the API is poll-based. The SmartSense web UI polls GET /devices every 2 seconds, which is a reasonable default for your own integrations too.
Endpoints
GET /devices
Lists every currently-connected device as a flat array — sensors plugged in directly over USB and sensors reached through a BLE bridge alike. Bridge-relayed devices are identical in shape apart from the viaBridge flag. Returns 200 with an array of DeviceRecord.
[ { "mac": "10:20:BA:70:E2:B0", "portPath": "COM4", "viaBridge": true, "connectedAt": "2026-09-05T14:02:11.483Z", "status": { "mac": "10:20:BA:70:E2:B0", "state": "BREATHING", "liquid": false, "mode": "ALARM_ON_DETECT", "color": { "r": 55, "g": 126, "b": 246 }, "alarmOutput": "BOTH", "name": "Reactor 3", "brightness": 90 } } ]
status is null only very briefly, between a device being detected and its first status line arriving — in practice that window is small enough to rarely be observed.
GET /devices/:mac
A single DeviceRecord, same shape as one entry of the list. Returns 200, or 404 with { "error": "device not found" }.
GET /devices/:mac/status
Just the status object for one device — a convenience for callers that only care about live sensor state, not connection metadata. Returns 200 with the DeviceStatus object (or null if the device is registered but hasn't reported a status yet), or 404 if the MAC isn't a known device at all.
POST /devices/:mac/config
Changes one or more settings on a device. All fields are optional, but at least one must be present. Whichever fields you include are sent to the device as SET_* commands over serial, or relayed through the bridge if that's how the device is reached.
This call returns as soon as the command is written to the wire, not once the device confirms — treat the response as accepted for delivery, not applied. A subsequent GET /devices/:mac/status reflects the change within roughly one poll/animation cycle: near-instant over direct USB, up to a few seconds over BLE.
POST /devices/10:20:BA:70:E2:B0/config Content-Type: application/json { "mode": "ALARM_ON_ABSENT", "color": { "r": 55, "g": 126, "b": 246 }, "brightness": 100 }
Config request body
Field Type Constraints
mode string "ALARM_ON_DETECT" or "ALARM_ON_ABSENT"
color object { r, g, b }, each an integer 0–255
alarmOutput string "BOTH", "VISUAL_ONLY", or "AUDIO_ONLY"
name string Max 20 characters, must not contain ;
brightness number Integer 0–120 (breathing peak brightness; alarm is always full brightness regardless)
Config responses
202 — { "accepted": true, "device": <DeviceRecord> }
404 — { "error": "device not found" }
400 — validation failure, with one of these messages: mode must be ALARM_ON_DETECT or ALARM_ON_ABSENT; color must be {r,g,b} integers 0-255; alarmOutput must be BOTH, VISUAL_ONLY, or AUDIO_ONLY; name must be a string of at most 20 characters, no ';'; brightness must be an integer 0-120; or, for an empty body, request body must include mode, color, alarmOutput, name, and/or brightness
The DeviceStatus object
Returned inside every DeviceRecord.status, and directly by GET /devices/:mac/status.
Field Type Notes
mac string Same canonical MAC as the parent record
state "BREATHING" | "ALARM" The wire protocol's original name. The web UI displays "BREATHING" as Monitoring; this field itself is unaffected by that relabeling
liquid boolean Raw sensor reading — true means liquid is physically detected, independent of mode
mode "ALARM_ON_DETECT" | "ALARM_ON_ABSENT" Which condition counts as an alarm
color { r, g, b } Current breathing color, 0–255 each. The alarm color is always red and isn't represented here
alarmOutput string "BOTH", "VISUAL_ONLY", or "AUDIO_ONLY" — which of the LED ring and buzzer actually fire during an alarm
name string User-assigned label; "" if never set
brightness number Breathing peak brightness, 0–120
Bridge-relayed devices
From this API's point of view, viaBridge devices behave identically to direct ones — same endpoints, same DeviceStatus shape. There are two operational differences worth designing around: BLE-relayed status and config round-trips take longer than direct USB, and a bridge-relayed device disappears from GET /devices a few seconds after it stops advertising, rather than instantly as it would on a USB unplug.
Sample code
Python
import requests BASE = "http://localhost:5177" MAC = "10:20:BA:70:E2:B0" # Poll every connected device for dev in requests.get(f"{BASE}/devices").json(): s = dev["status"] if s is None: continue # detected, but no status line yet label = s["name"] or dev["mac"] print(label, s["state"], "liquid" if s["liquid"] else "dry", "(bridge)" if dev["viaBridge"] else "(usb)") # Live status for one device status = requests.get(f"{BASE}/devices/{MAC}/status").json() # Reconfigure a device (202 = accepted for delivery, not yet applied) resp = requests.post(f"{BASE}/devices/{MAC}/config", json={ "mode": "ALARM_ON_ABSENT", "color": {"r": 55, "g": 126, "b": 246}, "brightness": 100, }) if resp.status_code == 400: print("rejected:", resp.json()["error"]) else: resp.raise_for_status()
C#
using System.Net.Http; using System.Net.Http.Json; using System.Text.Json.Serialization; record Rgb(int R, int G, int B); record DeviceStatus(string Mac, string State, bool Liquid, string Mode, Rgb Color, string AlarmOutput, string Name, int Brightness); record DeviceRecord(string Mac, string PortPath, bool ViaBridge, DateTimeOffset ConnectedAt, DeviceStatus? Status); var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; var client = new HttpClient { BaseAddress = new Uri("http://localhost:5177/") }; const string Mac = "10:20:BA:70:E2:B0"; // Poll every connected device var devices = await client.GetFromJsonAsync<List<DeviceRecord>>("devices", opts); foreach (var d in devices!) { if (d.Status is null) continue; // detected, but no status line yet var label = string.IsNullOrEmpty(d.Status.Name) ? d.Mac : d.Status.Name; Console.WriteLine($"{label} {d.Status.State} " + $"{(d.Status.Liquid ? "liquid" : "dry")} " + $"{(d.ViaBridge ? "(bridge)" : "(usb)")}"); } // Live status for one device var status = await client.GetFromJsonAsync<DeviceStatus>($"devices/{Mac}/status", opts); // Reconfigure a device (202 = accepted for delivery, not yet applied) var response = await client.PostAsJsonAsync($"devices/{Mac}/config", new { mode = "ALARM_ON_ABSENT", color = new { r = 55, g = 126, b = 246 }, brightness = 100, }); if (response.StatusCode == System.Net.HttpStatusCode.BadRequest) Console.WriteLine("rejected: " + await response.Content.ReadAsStringAsync()); else response.EnsureSuccessStatusCode();