Skip to main content
Version: Latest

Device Registry

The device registry maintains configuration and metadata for all physical equipment connected to the Flux platform.

Device Types​

BESS Devices​

Battery Energy Storage Systems are the primary controllable assets:

interface BESSDevice {
id: string; // UUID
name: string; // Human-readable identifier
type: 'bess';
site: string; // WLCE, HMCE, LFCE
metadata: {
manufacturer: string;
model: string;
capacity_kwh: number;
max_power_kw: number;
efficiency: number;
modbus_address: number;
firmware_version: string;
};
active: boolean;
created_at: Date;
updated_at: Date;
}

Example BESS Registration:

INSERT INTO flux.mg_device_registry (
id, name, type, site, metadata, active
) VALUES (
'11111111-1111-1111-1111-111111111111',
'WLCE-BESS-01',
'bess',
'WLCE',
'{
"manufacturer": "PowerPack",
"model": "PP-500",
"capacity_kwh": 500,
"max_power_kw": 100,
"efficiency": 0.92,
"modbus_address": 1,
"firmware_version": "2.1.3"
}'::jsonb,
true
);

Meter Devices​

Power meters monitor energy flows at key points:

interface MeterDevice {
id: string;
name: string;
type: 'meter';
site: string;
metadata: {
manufacturer: string;
model: string;
meter_type: 'grid' | 'generation' | 'load';
phases: number;
ct_ratio: number;
vt_ratio: number;
modbus_address: number;
register_map: string;
};
active: boolean;
}

Example Meter Registration:

INSERT INTO flux.mg_device_registry (
id, name, type, site, metadata, active
) VALUES (
'22222222-2222-2222-2222-222222222222',
'WLCE-GRID-METER',
'meter',
'WLCE',
'{
"manufacturer": "Accuenergy",
"model": "Acuvim-II",
"meter_type": "grid",
"phases": 3,
"ct_ratio": 100,
"vt_ratio": 1,
"modbus_address": 2,
"register_map": "acuvim2_standard"
}'::jsonb,
true
);

Device Lifecycle​

Registration Flow​

State Transitions​

From StateTo StateTriggerActions
RegisteredConfiguredConfig deploymentUpdate metadata
ConfiguredTestingFirst poll attemptVerify communication
TestingActiveSuccessful pollEnable telemetry
TestingFailedPoll timeout/errorAlert operator
ActiveMaintenanceScheduled/ManualDisable control
ActiveDecommissionedRemovalArchive data

Device Discovery​

Network Scanning​

# scan_devices.py
import minimalmodbus
import socket

def scan_modbus_network(subnet="192.168.1", port=502):
"""Scan network for Modbus devices"""
devices = []

for host in range(1, 255):
ip = f"{subnet}.{host}"
try:
# Test TCP connection
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(0.5)
result = sock.connect_ex((ip, port))
sock.close()

if result == 0:
# Try Modbus communication
for address in range(1, 10):
try:
instrument = minimalmodbus.Instrument(ip, address)
instrument.serial.timeout = 0.5
# Try reading holding register 0
value = instrument.read_register(0)
devices.append({
"ip": ip,
"port": port,
"address": address,
"responsive": True
})
print(f"Found device at {ip}:{port} address {address}")
except:
pass
except:
pass

return devices

Auto-Registration​

// auto_register.go
func autoRegisterDevice(ip string, modbusAddr uint8) (*Device, error) {
// Probe device type
deviceType := probeDeviceType(ip, modbusAddr)

// Generate registration
device := &Device{
ID: uuid.New(),
Name: fmt.Sprintf("%s-%s-%d", site, deviceType, modbusAddr),
Type: deviceType,
Site: site,
Metadata: map[string]interface{}{
"ip_address": ip,
"modbus_address": modbusAddr,
"discovered_at": time.Now(),
},
}

// Register in database
err := db.RegisterDevice(device)
return device, err
}

Device Grouping​

Logical Groups​

Devices can be organized into logical groups for coordinated control:

-- Create device groups table
CREATE TABLE flux.device_groups (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
site TEXT NOT NULL,
group_type TEXT NOT NULL, -- 'control', 'monitoring', 'analysis'
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Device group membership
CREATE TABLE flux.device_group_members (
group_id UUID REFERENCES flux.device_groups(id),
device_id UUID REFERENCES flux.mg_device_registry(id),
role TEXT, -- 'primary', 'backup', 'monitor'
priority INTEGER,
PRIMARY KEY (group_id, device_id)
);

Example Groups​

-- BESS fleet for coordinated dispatch
INSERT INTO flux.device_groups (name, site, group_type)
VALUES ('WLCE-BESS-Fleet', 'WLCE', 'control');

-- Critical meters for settlement
INSERT INTO flux.device_groups (name, site, group_type)
VALUES ('Settlement-Meters', 'ALL', 'monitoring');

Device Health Monitoring​

Health Metrics​

CREATE TABLE flux.device_health (
device_id UUID REFERENCES flux.mg_device_registry(id),
timestamp TIMESTAMPTZ NOT NULL,
status TEXT NOT NULL, -- 'online', 'offline', 'degraded'
response_time_ms INTEGER,
error_count INTEGER,
last_successful_poll TIMESTAMPTZ,
diagnostics JSONB,
PRIMARY KEY (device_id, timestamp)
);

-- Convert to hypertable
SELECT create_hypertable('flux.device_health', 'timestamp');

Health Check Implementation​

func checkDeviceHealth(device *Device) HealthStatus {
start := time.Now()

// Attempt communication
_, err := device.ReadRegister(0)
responseTime := time.Since(start).Milliseconds()

status := HealthStatus{
DeviceID: device.ID,
Timestamp: time.Now(),
ResponseTime: responseTime,
}

if err != nil {
status.Status = "offline"
status.ErrorCount++
} else if responseTime > 1000 {
status.Status = "degraded"
} else {
status.Status = "online"
status.LastSuccessfulPoll = time.Now()
}

return status
}

Device Configuration Management​

Configuration Templates​

# templates/powerpack_bess.yaml
device_template:
type: bess
manufacturer: PowerPack
default_config:
polling_interval: 60s
timeout: 5s
retry_count: 3
registers:
soe: 2026
target_power: 2008
status: 2000
available_blocks: 2028
scaling:
power: 1.0
energy: 0.001

Configuration Deployment​

# Deploy config to all BESS devices
flux devices apply-template \
--template powerpack_bess.yaml \
--filter "type=bess,site=WLCE" \
--commit

Maintenance Mode​

Setting Maintenance Mode​

-- Put device in maintenance
UPDATE flux.mg_device_registry
SET
active = false,
metadata = jsonb_set(
metadata,
'{maintenance}',
'{
"started_at": "2024-01-01T10:00:00Z",
"reason": "Firmware upgrade",
"expected_duration": "2 hours"
}'::jsonb
)
WHERE id = 'device-uuid';

Maintenance Notifications​

func enterMaintenanceMode(deviceID uuid.UUID, reason string) error {
// Update registry
device.Active = false
device.Metadata["maintenance"] = MaintenanceInfo{
StartedAt: time.Now(),
Reason: reason,
}

// Stop polling
controller.DisableDevice(deviceID)

// Send notifications
notifier.Send(fmt.Sprintf(
"Device %s entering maintenance: %s",
device.Name, reason
))

return nil
}

Next Steps​