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