class PfSenseCommandCenterCard extends HTMLElement {
setConfig(config) {
this.config = config;
// Track open/closed states for all four master accordion windows
this.sectionsOpen = this.sectionsOpen || {
control: false,
interfaces: false,
gateways: false,
services: false
};
this.logHistory = this.logHistory || [
"[SUCCESS] pfSense Command Engine synchronized successfully.",
"[INFO] All system monitoring modules active."
];
}
set hass(hass) {
this._hass = hass;
// --- UNIVERSAL AUTO-DISCOVERY ENGINE ---
const allEntityIds = Object.keys(hass.states);
// Intercept all pfSense metrics and core third-party infrastructure daemons
const pfEntities = allEntityIds.filter(id =>
id.startsWith('sensor.pfsense_') ||
id.startsWith('switch.pfsense_') ||
id.includes('_service_')
);
// Resolve structural system variables cleanly
const wanIpId = pfEntities.find(id => id.includes('wan_ip_address')) || '';
const dnsblId = pfEntities.find(id => id.includes('dnsbl_blocks')) || '';
const ipBlocksId = pfEntities.find(id => id.includes('ip_blocks')) || '';
const memId = pfEntities.find(id => id.includes('memory_used_percentage')) || '';
const dhcpId = pfEntities.find(id => id.includes('dhcp_leases_online')) || '';
const cpuId = pfEntities.find(id => id.includes('cpu_usage') || id.includes('cpu_used_percent')) || '';
const bootId = pfEntities.find(id => id.includes('system_boottime')) || '';
const wanIp = wanIpId && hass.states[wanIpId] ? hass.states[wanIpId].state : 'Disconnected';
const dnsbl = dnsblId && hass.states[dnsblId] ? hass.states[dnsblId].state : '0';
const ipBlocks = ipBlocksId && hass.states[ipBlocksId] ? hass.states[ipBlocksId].state : '0';
const memUsed = memId && hass.states[memId] ? hass.states[memId].state : '0';
const dhcpOnline = dhcpId && hass.states[dhcpId] ? hass.states[dhcpId].state : '0';
const cpuUsed = cpuId && hass.states[cpuId] ? hass.states[cpuId].state : '0';
let bootTimeStr = 'Unknown';
if (bootId && hass.states[bootId]) {
const rawBoot = hass.states[bootId].state;
bootTimeStr = rawBoot.includes('T') ? rawBoot.replace('T', ' ').substring(0, 16) : rawBoot;
}
// Process and sort nested items dynamically
const interfaces = {};
const gateways = {};
const services = [];
pfEntities.forEach(id => {
const stateObj = hass.states[id];
if (!stateObj) return;
if (id.includes('_interface_') && id.endsWith('_status')) {
const match = id.match(/_interface_(.*?)_status/);
if (match) {
const ifName = match[1];
interfaces[ifName] = { id: id, status: stateObj.state, props: {} };
pfEntities.filter(p => p.includes(`_interface_${ifName}_`)).forEach(pId => {
const propName = pId.split(`_interface_${ifName}_`)[1];
interfaces[ifName].props[propName] = hass.states[pId].state;
});
}
}
if (id.includes('_gateway_') && id.endsWith('_status')) {
const match = id.match(/_gateway_(.*?)_status/);
if (match) {
const gwName = match[1];
gateways[gwName] = { id: id, status: stateObj.state, props: {} };
pfEntities.filter(p => p.includes(`_gateway_${gwName}_`)).forEach(pId => {
const propName = pId.split(`_gateway_${gwName}_`)[1];
gateways[gwName].props[propName] = hass.states[pId].state;
});
}
}
if (id.startsWith('switch.') && (id.includes('_service_') || id.includes('pfsense_service_'))) {
services.push({ id: id, state: stateObj.state, friendlyName: stateObj.attributes.friendly_name || id });
}
});
// Draw the main card framework once to guarantee DOM stability
if (!this.content) {
this.innerHTML = `
Memory
${memUsed}%
RAM Load
CPU Load
${cpuUsed}%
Processor
DHCP Leases
${dhcpOnline}
Active Clients
External IP
${wanIp}
Border WAN
BOOTTIME MARKER: ${bootTimeStr}
Physical & Virtual Interfaces
Dynamic Gateway Routing Topology
Daemon Services & Tunnel Protection
`;
this.content = this.querySelector('#console-stream');
// Hook core routine executors safely
this.querySelector('#btn-add').addEventListener('click', () => this.fireAliasService('add'));
this.querySelector('#btn-rem').addEventListener('click', () => this.fireAliasService('remove'));
this.querySelector('#maint-flush').addEventListener('click', () => this.fireMaintenanceService('reset_state_table', 'FLUSH FIREWALL TABLES'));
this.querySelector('#maint-reboot').addEventListener('click', () => this.fireMaintenanceService('system_reboot', 'REBOOT APPLIANCE MACHINE'));
this.querySelector('#maint-halt').addEventListener('click', () => this.fireMaintenanceService('system_halt', 'POWER DOWN AND HALT THE GATEWAY ENGINE'));
// Bind all four accordion triggers to their layout drawers seamlessly
['control', 'interfaces', 'gateways', 'services'].forEach(key => {
this.querySelector(`#sec-${key} .section-trigger`).addEventListener('click', () => {
this.sectionsOpen[key] = !this.sectionsOpen[key];
this.querySelector(`#sec-${key}`).classList.toggle('open', this.sectionsOpen[key]);
});
});
}
// --- DIRECT HIGH-SPEED TELEMETRY SYNC LOOP ---
this.querySelector('#deck-mem').innerText = `${memUsed}%`;
this.querySelector('#deck-cpu').innerText = `${cpuUsed}%`;
this.querySelector('#deck-dhcp').innerText = dhcpOnline;
this.querySelector('#deck-wan').innerText = wanIp;
this.querySelector('#maint-boottime').innerText = `BOOTTIME MARKER: ${bootTimeStr}`;
// --- POPULATE AND RE-RENDER INTERFACES SUBGRID ---
const ifContainer = this.querySelector('#interfaces-container');
if (ifContainer) {
if (Object.keys(interfaces).length === 0) {
ifContainer.innerHTML = `
No active interfaces tracked.
`;
} else {
ifContainer.innerHTML = Object.keys(interfaces).map(name => {
const item = interfaces[name];
const cleanName = name.toUpperCase();
const speedIn = item.props['inbytes_kilobytes_per_second'] || '0';
const speedOut = item.props['outbytes_kilobytes_per_second'] || '0';
return `
Link ${cleanName}
Physical Port State Tracking
RX:${speedIn} KB/s
TX:${speedOut} KB/s
`;
}).join('');
}
}
// --- POPULATE AND RE-RENDER GATEWAYS SUBGRID ---
const gwContainer = this.querySelector('#gateways-container');
if (gwContainer) {
if (Object.keys(gateways).length === 0) {
gwContainer.innerHTML = `No active gateways discovered.
`;
} else {
gwContainer.innerHTML = Object.keys(gateways).map(name => {
const item = gateways[name];
const cleanName = name.replace('_status', '').toUpperCase();
const delay = item.props['delay'] || '0 ms';
const loss = item.props['loss'] || '0%';
return `
${cleanName}
Gateway Router Backhaul Link
`;
}).join('');
}
}
// --- POPULATE AND RE-RENDER SWITCH CONTROL ENGINE DECK ---
const srvContainer = this.querySelector('#services-container');
if (srvContainer) {
if (services.length === 0) {
srvContainer.innerHTML = `No network switches found.
`;
} else {
srvContainer.innerHTML = services.map(srv => {
const isActive = srv.state === 'on';
let label = srv.friendlyName.replace('pfSense ', '').replace('Service ', '').replace('status', '').replace('Status', '').trim();
return `
`;
}).join('');
// Wire service call bindings cleanly back to Home Assistant platform core
this.querySelectorAll('.deck-switch-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const entId = e.currentTarget.getAttribute('data-id');
const currentState = e.currentTarget.querySelector('p').innerText;
const targetService = currentState === 'on' ? 'turn_off' : 'turn_on';
const targetName = e.currentTarget.querySelector('h5').innerText;
if (confirm(`CRITICAL INFRASTRUCTURE WARNING:\nAre you sure you want to change network routing state for: ${targetName}?`)) {
this.addLogEntry(`[DAEMON] Modifying service path: ${targetName} -> ${targetService.toUpperCase()}`);
this._hass.callService('switch', targetService, { entity_id: entId });
}
});
});
}
}
const totalBlocks = parseInt(dnsbl) + parseInt(ipBlocks);
this.querySelector('#block-counter').innerText = `${totalBlocks} BLOCKS SINKHOLED`;
this.renderConsole();
}
addLogEntry(message) {
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19);
this.logHistory.unshift(`[${timestamp}] ${message}`);
if (this.logHistory.length > 15) this.logHistory.pop();
this.renderConsole();
}
renderConsole() {
if (!this.content) return;
this.content.innerHTML = this.logHistory.map(line => `${line}
`).join('');
}
async fireMaintenanceService(serviceName, warningText) {
const allEntityIds = Object.keys(this._hass.states);
const fallbackEntity = allEntityIds.find(id => id.startsWith('sensor.pfsense_') && id.includes('wan_ip_address')) || '';
if (!confirm(`CRITICAL APPLIANCE ALERT:\nAre you sure you want to execute command:\n-> [ ${warningText} ]?\n\nThis will instantly disrupt local network traffic.`)) {
return;
}
this.addLogEntry(`[MAINTENANCE] Firing system executive instruction: ${serviceName.toUpperCase()}`);
try {
await this._hass.callService('pfsense', serviceName, { entity_id: fallbackEntity });
this.addLogEntry(`[SUCCESS] Instruction handled by pfSense API Client.`);
} catch(err) {
this.addLogEntry(`[ERROR] Maintenance execution call dropped: ${err.message}`);
}
}
async fireAliasService(actionType) {
const aliasName = this.querySelector('#alias-name').value.trim();
const targetIp = this.querySelector('#target-ip').value.trim();
const allEntityIds = Object.keys(this._hass.states);
const fallbackEntity = allEntityIds.find(id => id.startsWith('sensor.pfsense_') && id.includes('wan_ip_address')) || '';
if (!aliasName || !targetIp) {
this.addLogEntry(`[WARNING] Input validation failed. Check parameter syntax variables.`);
return;
}
if (!confirm(`SECURITY ALERT: Alter alias tracking configuration row?\nTable: ${aliasName}\nTarget address: ${targetIp}\nOperation: ${actionType.toUpperCase()}`)) {
return;
}
this.addLogEntry(`[PENDING] Modifying table '${aliasName}': ${actionType.toUpperCase()} -> ${targetIp}`);
try {
await this._hass.callService('pfsense', 'update_alias', {
entity_id: fallbackEntity,
alias_name: aliasName,
address: targetIp,
action: actionType,
kill_states: true
});
this.addLogEntry(`[SUCCESS] Table written. Active states matching ${targetIp} terminated.`);
} catch (err) {
this.addLogEntry(`[ERROR] API transmission error: ${err.message}`);
}
}
getCardSize() {
return 8;
}
}
customElements.define('pfsense-command-center-card', PfSenseCommandCenterCard);