Skip to content

Hardcoded :8080 in run.py breaks machine connectivity and all commands #573

Description

@delitero

Summary

The MQTT bridge hardcodes port 8080 when connecting to the Meticulous machine. My machine serves its API through nginx on port 80 and has nothing listening on 8080, so the bridge can never connect.

This is not just a telemetry problem: the failed connection makes the bridge publish availability=offline, which the server treats as the machine being offline. The result is a permanent "machine offline" state in the UI and every machine command failing with HTTP 409.

Environment

Metic v2.6.0 (`https://www.google.com/url?q=http://ghcr.io/hessius/meticai:latest%60&source=gmail&ust=1785552303889000&sa=E)
Install unified Docker container, Linux host
Machine firmware 0.2.24-372-g7aa359b (software_version 2026-07-28 00:56:57)
Machine web server nginx/1.22.1 on port 80

What happens

The bridge builds its base URL with a hardcoded port:

# meticulous-addon/rootfs/usr/bin/run.py:294
base_url = f"http://{self.machine_ip}:8080"

There is no environment variable or UI setting to override this — METICULOUS_IP accepts only a host, never a port.

Resulting log loop:

run - WARNING - Socket.IO connection failed: HTTPConnectionPool(host='10.0.30.52', port=8080):
 Max retries exceeded with url: /https://www.google.com/url?q=http://socket.io/?transport%3Dpolling%26EIO%3D4&source=gmail&ust=1785552303889000&sa=E
 (Caused by NewConnectionError(... [Errno 111] Connection refused))
run - INFO - Connectivity state: disconnected
run - WARNING - Socket reconnection failed (attempt 12) ...

/api/v1/profile/list fails the same way.

Why it cascades

bridge cannot reach <machine>:8080
 └─> publishes availability=offline on MQTT
 └─> server/api/routes/commands.py:73 _require_connected()
 └─> HTTP 409 "Machine is offline" on every command
 └─> home page shows the machine as offline

So the port assumption breaks live status, live telemetry, Home Assistant discovery, and all machine control.

Notably, the server component gets this right — it addresses the machine without an explicit port (http://{ip}/api/v1/...), so shot history, shot analysis and profile generation all work correctly against the same machine. Only the components that append :8080 fail.

Evidence that the machine only serves port 80

From the Metic container:

$ curl -o /dev/null -w '%{http_code}' https://www.google.com/url?q=http://10.0.30.52:8080/api/v1/machine&source=gmail&ust=1785552303889000&sa=E
000 # connection refused

$ curl -o /dev/null -w '%{http_code}' https://www.google.com/url?q=http://10.0.30.52/api/v1/machine&source=gmail&ust=1785552303889000&sa=E
200
$ curl -I https://www.google.com/url?q=http://10.0.30.52/api/v1/machine&source=gmail&ust=1785552303889000&sa=E
HTTP/1.1 405 Method Not Allowed
Server: nginx/1.22.1
Access-Control-Allow-Origin: *

$ curl https://www.google.com/url?q=http://10.0.30.52/api/v1/machine&source=gmail&ust=1785552303889000&sa=E
{"name": "MeticulousDashingRiceMilk", "firmware": "0.2.24-372-g7aa359b", ...}

Socket.IO is served on port 80 as well and handshakes correctly there:

$ curl 'https://www.google.com/url?q=http://10.0.30.52/socket.io/?transport%3Dpolling%26EIO%3D4&source=gmail&ust=1785552303889000&sa=E'
0{"sid":"1PWZJDotLBBCeTPgAAAH","upgrades":["websocket"],"pingTimeout":60000,...}

Steps to reproduce

  1. Have a Meticulous machine on firmware 0.2.24 that serves its API via nginx on port 80.
  2. Install Metic v2.6.0 and set METICULOUS_IP to the machine's address.
  3. Open the home page — the machine shows as offline, permanently.
  4. docker logs meticai — Socket.IO reconnection failures against :8080, retrying forever.
  5. Any machine command returns 409 Machine is offline.

Other places with the same assumption

server/services/machine_discovery_service.py:206
 response = await client.get(f"http://{ip}:8080/api/getLastShotProfileJSON")

This presumably also prevents network auto-discovery from finding machines on this firmware. (I did not test discovery directly — I configured the IP manually.)

Suggested fix

Make the machine port configurable rather than hardcoded, e.g. a METICULOUS_PORT env var flowing into machine_port in options.json (bridge/start_bridge.py already maps METICULOUS_IP → machine_ip), defaulting to 8080 so existing setups are unaffected.

Alternatively, probe both ports on startup and use whichever answers — that would fix it with no user configuration, which matters because the failure is silent from the UI's perspective: it just says "offline" with no hint that a port is involved.

It would also help if the UI surfaced why the machine is considered offline, instead of only the final state.

Workaround (for anyone hitting this)

A TCP forwarder in front of the machine that answers on both ports, with METICULOUS_IP pointed at it:

services:
 metic-machine-proxy:
 image: alpine/socat:latest
 container_name: metic-machine-proxy
 restart: unless-stopped
 entrypoint: ["/bin/sh", "-c"]
 command:
 - |
 socat TCP-LISTEN:80,fork,reuseaddr TCP:${METICULOUS_MACHINE_IP}:80 &
 socat TCP-LISTEN:8080,fork,reuseaddr TCP:${METICULOUS_MACHINE_IP}:80 &
 wait -n
METICULOUS_MACHINE_IP=<real machine IP>
METICULOUS_IP=metic-machine-proxy

Plain TCP forwarding, so the Socket.IO websocket upgrade passes through intact. With this in place the bridge connects immediately, availability goes to online, and commands work:

run - INFO - Connected to MeticulousDashingRiceMilk (Serial: 013116)
run - INFO - Connectivity state: connected

Happy to test a patch against this firmware if that's useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions