en un clic
add-probe-detector-strix
// Add a new device type detector to the Strix probe system. Covers adding new probers, result types, and detector functions.
// Add a new device type detector to the Strix probe system. Covers adding new probers, result types, and detector functions.
Create or redesign frontend pages for Strix. Use when building new HTML pages, redesigning existing ones, or working on any UI task in the www/ directory. Covers design principles, layout patterns, and component usage.
Full release of Strix -- merge develop to main, tag, build multiarch Docker image, build static binaries, push to Docker Hub, update hassio-strix, create GitHub Release with binaries attached.
Add a new protocol support to Strix -- full flow from research to implementation. Covers stream handler registration, URL builder updates, database issues, and go2rtc integration.
Build and push dev Docker image for Strix, update hassio-strix dev add-on version.
| name | add_probe_detector_strix |
| description | Add a new device type detector to the Strix probe system. Covers adding new probers, result types, and detector functions. |
| disable-model-invocation | true |
| argument-hint | ["detector-name"] |
You are adding a new device type detector to the Strix probe system. The probe system runs when a user enters an IP address -- it discovers what's at that IP and determines the device type. The device type drives the frontend flow.
The detector name is provided as argument (e.g. /add_probe_detector_strix onvif). If no argument, use AskUserQuestion to ask which detector to add.
/home/user/Strix/home/user/go2rtcBefore writing anything, read these files COMPLETELY:
/home/user/Strix/internal/probe/probe.go -- glue: Init(), runProbe(), detectors, API handler
/home/user/Strix/pkg/probe/models.go -- all data structures (Response, Probes, result types)
/home/user/Strix/pkg/probe/ping.go -- prober example: ICMP ping
/home/user/Strix/pkg/probe/ports.go -- prober example: TCP port scan
/home/user/Strix/pkg/probe/arp.go -- prober example: ARP lookup
/home/user/Strix/pkg/probe/dns.go -- prober example: reverse DNS
/home/user/Strix/pkg/probe/http.go -- prober example: HTTP HEAD request
/home/user/Strix/pkg/probe/mdns.go -- prober example: HomeKit mDNS query
/home/user/Strix/pkg/probe/oui.go -- prober example: OUI vendor lookup
Read ALL of them. Every prober is different. Understand the full picture before proceeding.
The probe has three layers:
Layer 1: Probers (pkg/probe/)
Pure functions that gather raw data about an IP address. Each runs in parallel with a shared 100ms timeout context. They do NOT interpret results -- just collect facts.
Current probers:
Ping() -- ICMP echo, returns latencyScanPorts() -- TCP connect to all known camera ports, returns open portsReverseDNS() -- reverse DNS lookup, returns hostnameLookupARP() -- reads /proc/net/arp, returns MAC addressLookupOUI() -- looks up MAC prefix in SQLite, returns vendor nameProbeHTTP() -- HTTP HEAD to ports 80/8080, returns status + server headerQueryHAP() -- mDNS query for HomeKit Accessory Protocol, returns device infoEvery prober writes its result into resp.Probes.{Name} via mutex.
Layer 2: Detectors (internal/probe/probe.go)
Functions registered in the detectors slice. They run AFTER all probers complete. Each detector receives the full *probe.Response with all probe results and returns a device type string (or empty string to pass).
var detectors []func(*probe.Response) string
Detectors are checked in order. First non-empty result wins and sets resp.Type.
Default type is "standard". If device is unreachable, type is "unreachable".
Layer 3: API (internal/probe/probe.go)
GET /api/probe?ip=192.168.1.100 returns the full Response JSON. The frontend uses type field to decide which UI flow to show.
IP address
|
v
[All probers run in parallel, 100ms timeout]
|
v
probe.Response filled with results
|
v
[Detectors run in order on the Response]
|
v
resp.Type = "homekit" | "standard" | "unreachable" | ...
|
v
JSON response to frontend
{
"ip": "192.168.1.100",
"reachable": true,
"latency_ms": 2.5,
"type": "homekit",
"probes": {
"ping": {"latency_ms": 2.5},
"ports": {"open": [80, 554, 5353]},
"dns": {"hostname": "camera.local"},
"arp": {"mac": "C0:56:E3:AA:BB:CC", "vendor": "Hikvision"},
"mdns": {
"name": "My Camera",
"device_id": "AA:BB:CC:DD:EE:FF",
"model": "Camera 1080p",
"category": "camera",
"paired": false,
"port": 80
},
"http": {"port": 80, "status_code": 200, "server": "nginx"}
}
}
Use AskUserQuestion to discuss with the user. There are two scenarios:
The detector can determine device type from data already collected by existing probers. No new prober needed.
Examples:
In this case: skip to STEP 3.
Need to collect new data that existing probers don't provide. Requires adding a new prober to pkg/probe/ and a new result type to models.go.
Examples:
In this case: proceed to STEP 2.
Edit /home/user/Strix/pkg/probe/models.go:
type {Name}Result struct {
// fields specific to this probe
}
Probes struct:type Probes struct {
Ping *PingResult `json:"ping"`
Ports *PortsResult `json:"ports"`
DNS *DNSResult `json:"dns"`
ARP *ARPResult `json:"arp"`
MDNS *MDNSResult `json:"mdns"`
HTTP *HTTPResult `json:"http"`
{Name} *{Name}Result `json:"{name}"` // add here
}
Create /home/user/Strix/pkg/probe/{name}.go.
Rules:
context.Context and ip string as first params(*{Name}Result, error)nil, nil when device doesn't support this (NOT an error)Pattern:
package probe
import "context"
func Probe{Name}(ctx context.Context, ip string) (*{Name}Result, error) {
// respect context deadline
deadline, ok := ctx.Deadline()
if !ok {
// set sensible default
}
// do the probe work...
// not supported = nil, nil (not an error)
// found = &{Name}Result{...}, nil
// actual error = nil, err
}
Edit /home/user/Strix/internal/probe/probe.go, add to runProbe() alongside other probers:
run(func() {
r, _ := probe.Probe{Name}(ctx, ip)
mu.Lock()
resp.Probes.{Name} = r
mu.Unlock()
})
All probers run in parallel inside the same run() pattern. The mutex protects writes to resp.Probes.
Edit /home/user/Strix/internal/probe/probe.go, add detector in Init():
// {Name} detector
detectors = append(detectors, func(r *probe.Response) string {
// check probe results to determine device type
// return type string or "" to pass
if r.Probes.{Something} != nil && {condition} {
return "{type_name}"
}
return ""
})
"homekit", "onvif", "tapo", etc."" (empty) to pass to the next detectorThe type string is used by the frontend to select UI flow:
"unreachable" -- device not found (set automatically, don't return this)"standard" -- default, normal camera (set automatically if no detector matches)"homekit" -- Apple HomeKit devicecd /home/user/Strix
go build ./...
If it compiles, rebuild Docker and test:
docker build -t strix:test .
docker rm -f strix
docker run -d --name strix --network host --restart unless-stopped strix:test
sleep 2
# test probe on a known device
curl -s "http://localhost:4567/api/probe?ip={DEVICE_IP}" | python3 -m json.tool
Verify:
probes object (if new prober added)type field correctly identifies the devicedocker logs strixcd /home/user/Strix
git add -A
git commit -m "Add {name} probe detector"
git push origin develop
context.Context as first param for anything with I/Onil, nil for "not applicable" (not an error)conn, resp, bufomitempty