@@ -187,6 +187,10 @@ def mapped_devices(self):
187187 return drawn_devices
188188
189189 def resync_canvas (self ):
190+ """
191+ Synchronize canvas objects with current NetBox data.
192+ Handles both advanced mode (role/tenant/status) and original mode (status only) rack displays.
193+ """
190194 changed = False
191195 if self .canvas :
192196 if self .canvas .get ("objects" ):
@@ -202,19 +206,93 @@ def resync_canvas(self):
202206 else :
203207 rack = rack_qs .first ()
204208 self .canvas ["objects" ][index ]["custom_meta" ]["object_name" ] = rack .name
209+
210+ # Update rack fill color based on role (only if not manually set
211+ # and for advanced racks only)
212+ # Check if color was manually set by looking for manual_color flag
213+ color_manually_set = obj ["custom_meta" ].get ("manual_color" , False )
214+
215+ # Detect if this is an advanced rack by checking for info text type
216+ is_advanced_mode_rack = False
217+ if obj .get ("objects" ):
218+ for subobj in obj ["objects" ]:
219+ if (subobj .get ("type" ) in ["i-text" , "textbox" ] and
220+ subobj .get ("custom_meta" , {}).get ("text_type" ) == "info" ):
221+ is_advanced_mode_rack = True
222+ break
223+
224+ # Only apply automatic color updates to advanced racks
225+ if not color_manually_set and is_advanced_mode_rack :
226+ expected_color = None
227+ if rack .role and hasattr (rack .role , 'color' ):
228+ expected_color = f"#{ rack .role .color } "
229+ else :
230+ # Default color if no role or color is set
231+ expected_color = "#000000"
232+
233+ # Check if the rack rectangle color needs updating
234+ if obj .get ("objects" ) and len (obj ["objects" ]) > 0 :
235+ rack_rect = obj ["objects" ][0 ] # First object is typically the rack rectangle
236+ if rack_rect .get ("fill" ) != expected_color :
237+ self .canvas ["objects" ][index ]["objects" ][0 ]["fill" ] = expected_color
238+ changed = True
239+ # End of rack fill color update
240+
205241 if obj .get ("objects" ):
206242 for subcounter , subobj in enumerate (obj ["objects" ]):
207- if subobj .get ("type" ) == "i-text" :
243+ # Check if the subobject is a rectangle and has custom_meta for rack
244+ # Update the custom_meta and text fields to match the current rack data
245+ # in Netbox
246+ if subobj .get ("type" ) == "rect" :
247+ if subobj .get ("custom_meta" , {}).get ("object_type" ) == "rack" :
248+ # Make sure the object_name matches the actual rack name
249+ if subobj ["custom_meta" ]["object_name" ] != f"{ rack .name } " :
250+ self .canvas ["objects" ][index ]["objects" ][
251+ subcounter ]["custom_meta" ]["object_name" ] = f"{ rack .name } "
252+ changed = True
253+
254+ # Check if the subobject is a textbox or i-text object. This will have both
255+ # the rack name and the info text (status, role, tenant for advanced racks
256+ # or just status for simple racks).
257+ if subobj .get ("type" ) == "i-text" or subobj .get ("type" ) == "textbox" :
258+ # Update the name text box with the current rack name if it exists
208259 if subobj .get ("custom_meta" , {}).get ("text_type" ) == "name" :
209260 if subobj ["text" ] != f"{ rack .name } " :
210261 self .canvas ["objects" ][index ]["objects" ][
211262 subcounter ]["text" ] = f"{ rack .name } "
212263 changed = True
213- if subobj .get ("custom_meta" , {}).get ("text_type" ) == "status" :
264+ # Handle advanced racks combined info text box
265+ elif subobj .get ("custom_meta" , {}).get ("text_type" ) == "info" :
266+ # Handle combined info text box (advanced mode)
267+ rack_role_text = rack .role .name if rack .role else ""
268+ rack_tenant_text = f"{ rack .tenant } " if rack .tenant else ""
269+
270+ # Update stored values in custom_meta
271+ subobj ["custom_meta" ]["status" ] = f"{ rack .status } "
272+ subobj ["custom_meta" ]["role" ] = rack_role_text
273+ subobj ["custom_meta" ]["tenant" ] = rack_tenant_text
274+
275+ # Rebuild the combined text based on visibility settings
276+ info_lines = []
277+ if subobj ["custom_meta" ].get ("show_status" , True ):
278+ info_lines .append (f"{ rack .status } " )
279+ if subobj ["custom_meta" ].get ("show_role" , True ) and rack_role_text :
280+ info_lines .append (rack_role_text )
281+ if subobj ["custom_meta" ].get ("show_tenant" , True ) and rack_tenant_text :
282+ info_lines .append (rack_tenant_text )
283+
284+ new_text = '\n ' .join (info_lines )
285+ if subobj ["text" ] != new_text :
286+ self .canvas ["objects" ][index ]["objects" ][subcounter ]["text" ] = new_text
287+ changed = True
288+ # Handle simple racks status text box, which only shows status
289+ elif subobj .get ("custom_meta" , {}).get ("text_type" ) == "status" :
214290 if subobj ["text" ] != f"{ rack .status } " :
215291 self .canvas ["objects" ][index ]["objects" ][
216292 subcounter ]["text" ] = f"{ rack .status } "
217293 changed = True
294+
295+ # Handle device objects on the canvas
218296 if obj ["custom_meta" ].get ("object_type" ) == "device" :
219297 device_id = int (obj ["custom_meta" ]["object_id" ])
220298 # if device is not in the database, remove it from the canvas
@@ -227,13 +305,45 @@ def resync_canvas(self):
227305 self .canvas ["objects" ][index ]["custom_meta" ]["object_name" ] = device .name
228306 if obj .get ("objects" ):
229307 for subcounter , subobj in enumerate (obj ["objects" ]):
230- if subobj .get ("type" ) == "i-text" :
308+ # Update device rectangle metadata
309+ if subobj .get ("type" ) == "rect" :
310+ if subobj .get ("custom_meta" , {}).get ("object_type" ) == "device" :
311+ # Make sure the object_name matches the actual device name
312+ if subobj ["custom_meta" ]["object_name" ] != f"{ device .name } " :
313+ self .canvas ["objects" ][index ]["objects" ][
314+ subcounter ]["custom_meta" ]["object_name" ] = f"{ device .name } "
315+ changed = True
316+ # Update device text elements (supports both advanced and simple devices)
317+ if subobj .get ("type" ) == "i-text" or subobj .get ("type" ) == "textbox" :
318+
319+ # Update device name text
231320 if subobj .get ("custom_meta" , {}).get ("text_type" ) == "name" :
232321 if subobj ["text" ] != f"{ device .name } " :
233322 self .canvas ["objects" ][index ]["objects" ][
234323 subcounter ]["text" ] = f"{ device .name } "
235324 changed = True
236- if subobj .get ("custom_meta" , {}).get ("text_type" ) == "status" :
325+ # Handle advanced devices combined info text box for devices
326+ elif subobj .get ("custom_meta" , {}).get ("text_type" ) == "info" :
327+ # Handle combined info text box for devices (advanced mode)
328+ device_tenant_text = f"{ device .tenant } " if device .tenant else ""
329+
330+ # Update stored values in custom_meta
331+ subobj ["custom_meta" ]["status" ] = f"{ device .status } "
332+ subobj ["custom_meta" ]["tenant" ] = device_tenant_text
333+
334+ # Rebuild the combined text based on visibility settings
335+ info_lines = []
336+ if subobj ["custom_meta" ].get ("show_status" , True ):
337+ info_lines .append (f"{ device .status } " )
338+ if subobj ["custom_meta" ].get ("show_tenant" , True ) and device_tenant_text :
339+ info_lines .append (device_tenant_text )
340+
341+ new_text = '\n ' .join (info_lines )
342+ if subobj ["text" ] != new_text :
343+ self .canvas ["objects" ][index ]["objects" ][subcounter ]["text" ] = new_text
344+ changed = True
345+ # Handle 'simple' devices status text box
346+ elif subobj .get ("custom_meta" , {}).get ("text_type" ) == "status" :
237347 if subobj ["text" ] != f"{ device .status } " :
238348 self .canvas ["objects" ][index ]["objects" ][
239349 subcounter ]["text" ] = f"{ device .status } "
0 commit comments