Fix onboard BLE admission and stage wireless device enrollment

This commit is contained in:
DCCONSTRUCTIONS
2026-09-07 12:11:48 +03:00
parent 2fdf1d3cc4
commit 77acb377e2
17 changed files with 297 additions and 65 deletions
@@ -105,7 +105,6 @@ export interface DevicePluginConnectionProps {
export interface DeviceUiPlugin {
sensorUi?: {
contributions: readonly import('../../../../../packages/sensor-ui/src/extensions').SensorUiContribution[];
Enrollment?: ComponentType<import('../../../../../packages/sensor-ui/src/extensions').SensorEnrollmentProps>;
};
manifest: DevicePluginManifest;
RuntimeProvider: ComponentType<{
@@ -7,8 +7,6 @@ import {fleetRequest} from '../../core/fleet/useFleet';
export function VehicleSensors({vehicleID,enabled,onDetailChange}:{vehicleID:string;enabled:boolean;onDetailChange:(open:boolean)=>void}){
const {registry}=useDevicePluginHost();
const sensorContributions=useMemo(()=>registry.plugins.flatMap(plugin=>plugin.sensorUi?.contributions??[]),[registry]);
const enrollmentViews=registry.plugins.flatMap(plugin=>plugin.sensorUi?.Enrollment?[plugin.sensorUi.Enrollment]:[]);
const SensorEnrollmentView=enrollmentViews.length===1?enrollmentViews[0]:undefined;
const transport=useMemo<SensorTransport>(()=>({
enrollment:{
state:()=>fleetRequest(`/${encodeURIComponent(vehicleID)}/devices/enrollment`),
@@ -20,5 +18,5 @@ export function VehicleSensors({vehicleID,enabled,onDetailChange}:{vehicleID:str
submit:value=>fleetRequest(`/${encodeURIComponent(vehicleID)}/devices/operations`,'POST',value),
operation:id=>fleetRequest(`/${encodeURIComponent(vehicleID)}/devices/operations/${encodeURIComponent(id)}`),
}),[vehicleID]);
return <SensorWorkspace contributions={sensorContributions} EnrollmentView={SensorEnrollmentView} createRerunHost={createIsolatedRerunHost} key={vehicleID} transport={transport} enabled={enabled} onDetailChange={onDetailChange}/>;
return <SensorWorkspace contributions={sensorContributions} createRerunHost={createIsolatedRerunHost} key={vehicleID} transport={transport} enabled={enabled} onDetailChange={onDetailChange}/>;
}
@@ -2,11 +2,12 @@ import assert from 'node:assert/strict';
import {before,after,test} from 'node:test';
import {readFileSync,readdirSync} from 'node:fs';
import {createServer} from 'vite';
let server,api,resolveContribution;
let server,api,resolveContribution,wirelessContributions;
before(async()=>{
server=await createServer({appType:'custom',logLevel:'silent',server:{middlewareMode:true}});
api=await server.ssrLoadModule('@xgrids-k1/frontend/sensors/enrollment.ts');
({sensorContribution:resolveContribution}=await server.ssrLoadModule('../../packages/sensor-ui/src/extensions.ts'));
({wirelessContributions}=await server.ssrLoadModule('../../packages/sensor-ui/src/extensions.ts'));
});
after(async()=>{await server?.close();});
const initial={node_id:'node-one',available:true,fresh:true,runtime_id:'runtime-one',snapshot_revision:1,runtime_started_at:'2026-09-06T00:00:00Z',selected_device_id:'synthetic-ble'};
@@ -87,6 +88,14 @@ test('sensor host can resolve zero or unrelated integrations and rejects ambigui
assert.doesNotMatch(readFileSync(new URL('../../../packages/sensor-ui/src/'+name,import.meta.url),'utf8'),/xgrids|lixel|\bk1\b|K1Detail/,'vendor dependency in '+name);
}
});
test('wireless device choices come from installed contributions without a single-vendor slot',()=>{
const wired={kind:'wired',Detail:()=>null};
const alpha={kind:'alpha',wirelessEnrollment:{label:'Device alpha',View:()=>null}};
const beta={kind:'beta',wirelessEnrollment:{label:'Device beta',View:()=>null}};
assert.deepEqual(wirelessContributions([]),[]);
assert.deepEqual(wirelessContributions([wired,alpha,beta]),[alpha,beta]);
assert.deepEqual(wirelessContributions([alpha,{...alpha},beta]),[beta]);
});
test('onboard actions use current server policy and never infer authority from readiness',()=>{
const state={...initial,connected:true,allowed_actions:['verify-control-read-only']};
assert.equal(api.enrollmentAllowed(state,'connect'),false);