fix(relays): {value} is the relay's label, not a per-relay value
It was built the other way round on a misreading of two screenshots: the pattern
held {value} and the per-relay boxes held numbers to drop into it. The ask was
simpler and better — {value} is the name typed in Relay labels, so a switch
addressed by antenna name is one pattern instead of eight URLs:
http://10.10.10.100/relay?on={value} relay 1 named Ant1 → ?on=Ant1
Renaming the antenna re-addresses it, and the name on the button and the name on
the wire cannot drift apart because they are the same string. It works in the
per-relay URLs and in the patterns alike, so the per-relay boxes go back to
holding URLs and nothing about them changes meaning any more.
The label is percent-encoded with %20 rather than "+" for a space: "+" is a
space only in a query string and a literal plus in a path, and this can land in
either half of a URL.
{value} on a relay with no label would send "?on=", an empty parameter that most
boards answer with a cheerful 200 and no movement. The driver refuses it and
names the label as what is missing; the editor warns while it is being typed,
beside the empty box rather than after an antenna fails to switch. The labels
also join the driver's cache key — they are part of the wire format now.
This commit is contained in:
@@ -696,11 +696,13 @@ function DeviceEditor({ device, onChange, onSave, onCancel, t }: {
|
||||
const isDenkovi = device.type === 'denkovi';
|
||||
const isUsbRelay = device.type === 'usbrelay';
|
||||
const isHTTPGen = device.type === 'httpgen';
|
||||
// {value} in a pattern changes what the boxes below hold — a value to drop
|
||||
// into it instead of a whole URL. The grid says which as soon as it is typed,
|
||||
// because the two are indistinguishable once entered and getting it wrong
|
||||
// switches an antenna somewhere unexpected.
|
||||
const usesValue = `${device.on_pattern ?? ''}${device.off_pattern ?? ''}`.includes('{value}');
|
||||
// {value} sends a relay's label, so a URL using it on an unnamed relay would
|
||||
// go out with an empty parameter. Warn while it is being typed rather than at
|
||||
// the moment an antenna fails to switch.
|
||||
const valueNeedsLabels = isHTTPGen
|
||||
&& [...(device.on_urls ?? []), ...(device.off_urls ?? []), device.on_pattern ?? '', device.off_pattern ?? '']
|
||||
.some((s) => (s ?? '').includes('{value}'))
|
||||
&& device.labels.some((l) => !l.trim());
|
||||
// COM ports for the generic USB-serial relay picker.
|
||||
const [serialPorts, setSerialPorts] = useState<string[]>([]);
|
||||
useEffect(() => {
|
||||
@@ -874,12 +876,12 @@ function DeviceEditor({ device, onChange, onSave, onCancel, t }: {
|
||||
</div>
|
||||
<div className="text-[10px] text-muted-foreground">{t('station.patternHint')}</div>
|
||||
<div className="space-y-1">
|
||||
<Label>{usesValue ? t('station.perRelayValues') : t('station.perRelayUrls')}</Label>
|
||||
<Label>{t('station.perRelayUrls')}</Label>
|
||||
<div className="space-y-1">
|
||||
{device.labels.map((_, i) => (
|
||||
<div key={i} className="grid grid-cols-[2.5rem_1fr_1fr] items-center gap-2">
|
||||
<span className="text-[11px] text-muted-foreground">{i + 1}</span>
|
||||
<Input className="h-8 font-mono text-[11px]" placeholder={usesValue ? t('station.onValuePh') : t('station.onUrlPh')}
|
||||
<Input className="h-8 font-mono text-[11px]" placeholder={t('station.onUrlPh')}
|
||||
value={device.on_urls?.[i] ?? ''}
|
||||
onChange={(e) => {
|
||||
const on_urls = [...(device.on_urls ?? [])];
|
||||
@@ -887,7 +889,7 @@ function DeviceEditor({ device, onChange, onSave, onCancel, t }: {
|
||||
on_urls[i] = e.target.value;
|
||||
onChange({ ...device, on_urls });
|
||||
}} />
|
||||
<Input className="h-8 font-mono text-[11px]" placeholder={usesValue ? t('station.offValuePh') : t('station.offUrlPh')}
|
||||
<Input className="h-8 font-mono text-[11px]" placeholder={t('station.offUrlPh')}
|
||||
value={device.off_urls?.[i] ?? ''}
|
||||
onChange={(e) => {
|
||||
const off_urls = [...(device.off_urls ?? [])];
|
||||
@@ -898,13 +900,17 @@ function DeviceEditor({ device, onChange, onSave, onCancel, t }: {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-[10px] text-muted-foreground">{usesValue ? t('station.perValueHint') : t('station.perRelayHint')}</div>
|
||||
<div className="text-[10px] text-muted-foreground">{t('station.perRelayHint')}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-1">
|
||||
<Label>{t('station.labels')}</Label>
|
||||
{/* {value} sends the label, so an unnamed relay would go out as "?on=".
|
||||
Said here, beside the empty box, rather than when the antenna fails
|
||||
to switch and the log is the only place that explains why. */}
|
||||
{valueNeedsLabels && <p className="text-[10px] text-warning">{t('station.valueNeedsLabels')}</p>}
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
{device.labels.map((lab, i) => (
|
||||
<Input key={i} value={lab} placeholder={`${t('station.relay')} ${i + 1}`} className="h-8 text-xs"
|
||||
|
||||
Reference in New Issue
Block a user