Voici le code
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Diagramme de Réseau Domestique</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/MaterialDesign-Webfont/7.2.96/css/materialdesignicons.min.css" rel="stylesheet">
<!-- Styles -->
<style>
#network-diagram {
width: 100%;
height: 800px;
position: relative;
overflow: hidden;
background: transparent;
}
.node {
cursor: move;
}
.icon-container {
width: 48px;
height: 48px;
background: white;
border: 2px solid #666;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.3s ease;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.icon-container:hover {
transform: scale(1.1);
border-color: #4CAF50;
box-shadow: 0 4px 12px rgba(76,175,80,0.3);
background: rgba(76,175,80,0.1);
}
.mdi {
font-size: 20px;
color: #4CAF50;
}
.ip-label {
font-size: 10px;
text-anchor: middle;
fill: currentColor;
}
.title-label {
font-size: 12px;
text-anchor: middle;
font-weight: bold;
fill: currentColor;
}
foreignObject {
overflow: visible;
}
.link-highlight {
stroke: #4CAF50 !important;
stroke-opacity: 1 !important;
stroke-width: 2px !important;
}
</style>
</head>
<body>
<div id="network-diagram"></div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
// =============================================
// DÉFINITION DES ÉQUIPEMENTS
// =============================================
const deviceGroups = {
network: { name: "Réseau", icon: "mdi-router-network", devices: [
{ name: "SynologyNAS-13", ip: "192.168.1.2", icon: "mdi-nas" },
{ name: "MR2200ac-BWQQT0-Chambre", ip: "192.168.1.254", icon: "mdi-wifi" },
{ name: "MR2200ac-PRCAJV-Piscine", ip: "192.168.1.253", icon: "mdi-wifi" },
{ name: "XiaoMiRepeater_V2", ip: "192.168.1.251", icon: "mdi-wifi" }
]},
cameras: { name: "Caméras", icon: "mdi-cctv", devices: [
{ name: "CameraAxisPiscine-POE4", ip: "192.168.1.7", icon: "mdi-cctv" },
{ name: "CameraGarage-POE1", ip: "192.168.1.3", icon: "mdi-cctv" },
{ name: "CameraMobile", ip: "192.168.1.8", icon: "mdi-cctv" },
{ name: "CameraMobotixBassin", ip: "192.168.1.4", icon: "mdi-cctv" },
{ name: "CameraPorche-10", ip: "192.168.1.5", icon: "mdi-cctv" },
{ name: "CameraVoitures-POE2", ip: "192.168.1.6", icon: "mdi-cctv" }
]},
domotique: { name: "Domotique", icon: "mdi-home-automation", devices: [
{ name: "HomeAssistant", ip: "192.168.1.222", icon: "mdi-home-assistant" },
{ name: "Jeedom-Maitre", ip: "192.168.1.100", icon: "mdi-home-automation" },
{ name: "Jeedom-Esclave", ip: "192.168.1.21", icon: "mdi-home-automation" },
{ name: "jeedomchauffage", ip: "192.168.1.150", icon: "mdi-home-automation" }
]},
alexa: { name: "Alexas", icon: "mdi-amazon-alexa", devices: [
{ name: "ChambreParents", ip: "192.168.1.142", icon: "mdi-amazon-alexa" },
{ name: "Coralie", ip: "192.168.1.136", icon: "mdi-amazon-alexa" },
{ name: "CouloirEtage", ip: "192.168.1.143", icon: "mdi-amazon-alexa" },
{ name: "Cuisine", ip: "192.168.1.137", icon: "mdi-amazon-alexa" },
{ name: "Rose", ip: "192.168.1.138", icon: "mdi-amazon-alexa" },
{ name: "SalledeBains", ip: "192.168.1.135", icon: "mdi-amazon-alexa" },
{ name: "SalonRdc", ip: "192.168.1.139", icon: "mdi-amazon-alexa" }
]},
computers: { name: "Ordinateurs", icon: "mdi-desktop-classic", devices: [
{ name: "PC-Christel-Eth", ip: "192.168.1.35", icon: "mdi-desktop-classic" },
{ name: "PC-Christel-Wifi", ip: "192.168.1.34", icon: "mdi-desktop-classic" },
{ name: "PC-Coralie", ip: "192.168.1.29", icon: "mdi-desktop-classic" },
{ name: "PC-Jeedom-Cuisine", ip: "192.168.1.31", icon: "mdi-desktop-classic" },
{ name: "PC-Lionel-2", ip: "192.168.1.30", icon: "mdi-desktop-classic" },
{ name: "OrdiCoralie", ip: "192.168.1.28", icon: "mdi-desktop-classic" }
]},
mobile: { name: "Mobiles", icon: "mdi-cellphone", devices: [
{ name: "Galaxy-J5-2016", ip: "192.168.1.163", icon: "mdi-cellphone" },
{ name: "Galaxy-S10", ip: "192.168.1.164", icon: "mdi-cellphone" },
{ name: "Galaxy-Tab-A", ip: "192.168.1.171", icon: "mdi-tablet" },
{ name: "GalaxyJ7-Christel", ip: "192.168.1.160", icon: "mdi-cellphone" },
{ name: "GalaxyS10-Lionel", ip: "192.168.1.161", icon: "mdi-cellphone" },
{ name: "GalaxyS7-Coralie", ip: "192.168.1.162", icon: "mdi-cellphone" },
{ name: "iPad-de-user", ip: "192.168.1.170", icon: "mdi-tablet-ipad" }
]},
appliances: { name: "Appareils", icon: "mdi-television", devices: [
{ name: "Apple-TV", ip: "192.168.1.232", icon: "mdi-apple" },
{ name: "Apple-TV-Wifi", ip: "192.168.1.233", icon: "mdi-apple" },
{ name: "FireTV-Chambre", ip: "192.168.1.146", icon: "mdi-fire" },
{ name: "FireTV-Chambre-Eth", ip: "192.168.1.144", icon: "mdi-fire" },
{ name: "TVChambre-Wifi", ip: "192.168.1.147", icon: "mdi-television" }
]},
automation: { name: "Automatisation", icon: "mdi-home", devices: [
{ name: "Arrosage", ip: "192.168.1.91", icon: "mdi-sprinkler-variant" },
{ name: "Chauffage", ip: "192.168.1.116", icon: "mdi-thermostat" },
{ name: "ChauffeEau", ip: "192.168.1.89", icon: "mdi-water-boiler" },
{ name: "Portail", ip: "192.168.1.82", icon: "mdi-gate" },
{ name: "WallBox", ip: "192.168.1.133", icon: "mdi-ev-station" }
]},
diy: { name: "DIY", icon: "mdi-chip", devices: [
{ name: "DIYSalon", ip: "192.168.1.211", icon: "mdi-home-automation" },
{ name: "DIYSalon2", ip: "192.168.1.212", icon: "mdi-home-automation" },
{ name: "DIYPenderie", ip: "192.168.1.214", icon: "mdi-home-automation" },
{ name: "DIYCuisine", ip: "192.168.1.215", icon: "mdi-home-automation" },
{ name: "DIYExtCuisine", ip: "192.168.1.216", icon: "mdi-home-automation" },
{ name: "DIYExtGarage", ip: "192.168.1.217", icon: "mdi-home-automation" },
{ name: "DIYChParents", ip: "192.168.1.220", icon: "mdi-home-automation" },
{ name: "DIYPOWR3", ip: "192.168.1.221", icon: "mdi-home-automation" }
]},
other: { name: "Autres", icon: "mdi-devices", devices: [
{ name: "PorteEntree", ip: "192.168.1.9", icon: "mdi-door" },
{ name: "Portier-POE3", ip: "192.168.1.10", icon: "mdi-doorbell" },
{ name: "PasserelleTuyaZigbee", ip: "192.168.1.245", icon: "mdi-zigbee" },
{ name: "thermomix-236844", ip: "192.168.1.246", icon: "mdi-blender" },
{ name: "S685IP-Telephones-8", ip: "192.168.1.249", icon: "mdi-phone" },
{ name: "Energy", ip: "192.168.1.225", icon: "mdi-flash" },
{ name: "Eau", ip: "192.168.1.230", icon: "mdi-water" }
]}
};
// =============================================
// CODE DE VISUALISATION
// =============================================
document.addEventListener("DOMContentLoaded", function() {
const svg = d3.select("#network-diagram")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", "-400 -400 800 800");
// Créer les nœuds et liens
const nodes = [];
const links = [];
// Ajouter Internet
nodes.push({
id: "Internet",
group: "internet",
icon: "mdi-web",
name: "Internet",
fx: -50,
fy: -200
});
// Ajouter la Freebox
nodes.push({
id: "Freebox",
name: "Freebox",
ip: "192.168.1.255",
icon: "mdi-router",
group: "internet-devices",
fx: -25,
fy: -100
});
// Ajouter le routeur principal
nodes.push({
id: "Router",
group: "center",
icon: "mdi-router-wireless",
name: "Routeur Principal",
ip: "192.168.1.1",
fx: 0,
fy: 0
});
// Ajouter les liens Internet -> Freebox -> Router
links.push({ source: "Internet", target: "Freebox", type: "internet" });
links.push({ source: "Freebox", target: "Router" });
// Ajouter les groupes et leurs appareils
let angle = 0;
const angleStep = (2 * Math.PI) / Object.keys(deviceGroups).length;
const groupRadius = 80;
Object.entries(deviceGroups).forEach(([key, group]) => {
const groupX = Math.cos(angle) * groupRadius;
const groupY = Math.sin(angle) * groupRadius;
nodes.push({
id: key,
group: "group",
icon: group.icon,
name: group.name,
x: groupX,
y: groupY
});
links.push({ source: "Router", target: key });
const deviceAngleStep = (2 * Math.PI) / group.devices.length;
group.devices.forEach((device, i) => {
const deviceAngle = angle + i * deviceAngleStep;
nodes.push({
id: device.name,
group: key,
icon: device.icon,
name: device.name,
ip: device.ip
});
links.push({ source: key, target: device.name });
});
angle += angleStep;
});
// Configuration de la simulation
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id)
.distance(d => {
if (d.type === "internet") return 100;
if (d.source.group === "center") {
return 50;
} else if (d.source.group === "group") {
return 150;
}
return 50;
}))
.force("charge", d3.forceManyBody().strength(-300))
.force("center", d3.forceCenter(0, 0))
.force("collision", d3.forceCollide().radius(30));
// Dessiner les liens
const link = svg.append("g")
.selectAll("line")
.data(links)
.join("line")
.attr("stroke", d => d.type === "internet" ? "#4CAF50" : "#666")
.attr("stroke-opacity", d => d.type === "internet" ? 1 : 0.4)
.attr("stroke-width", d => {
if (d.type === "internet") return 3;
return d.source.group === "center" ? 3 : 3;
})
.attr("stroke-dasharray", d => d.type === "internet" ? "5,5" : "none");
// Fonction de drag
const drag = d3.drag()
.on("start", (event, d) => {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
})
.on("drag", (event, d) => {
d.fx = event.x;
d.fy = event.y;
})
.on("end", (event, d) => {
if (!event.active) simulation.alphaTarget(0);
if (d.id !== "Router" && d.id !== "Internet" && d.id !== "Freebox") {
d.fx = null;
d.fy = null;
}
});
// Dessiner les nœuds
const node = svg.append("g")
.selectAll("g")
.data(nodes)
.join("g")
.attr("class", "node")
.call(drag)
.on("mouseover", (event, d) => {
// Mettre en surbrillance les liens connectés
link.classed("link-highlight", l =>
l.source.id === d.id || l.target.id === d.id
);
})
.on("mouseout", () => {
// Retirer la surbrillance
link.classed("link-highlight", false);
});
// Ajouter les icônes
node.append("foreignObject")
.attr("width", 40)
.attr("height", 40)
.attr("x", -20)
.attr("y", -20)
.append("xhtml:div")
.attr("class", "icon-container")
.html(d => `<i class="mdi ${d.icon}"></i>`);
// Ajouter les étiquettes de nom
node.append("text")
.attr("class", "title-label")
.attr("dy", -25)
.text(d => d.name);
// Ajouter les étiquettes IP
node.append("text")
.attr("class", "ip-label")
.attr("dy", 35)
.text(d => d.ip || "");
// Mise à jour de la position
simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node.attr("transform", d => `translate(${d.x},${d.y})`);
});
});
</script>
</body>
</html>