Automatic pipe insulation

Hello,
am trying to create a tool to automatically add insulation on objects. the goal is to add insulation depending on the value of the systeme type.
so far i’ve written this:

"# coding: utf-8
import clr
from rpw import revit
from Autodesk.Revit.UI.Selection import ObjectType
from Autodesk.Revit.DB.Plumbing import PipeInsulation
from Autodesk.Revit.DB import *
from pyrevit import forms
from pyrevit import DB

doc = revit.doc
uidoc = revit.uidoc

Demander à l’utilisateur de sélectionner des éléments

try:
with forms.WarningBar(title=“Sélectionnez les éléments à isoler”):
references = uidoc.Selection.PickObjects(ObjectType.Element)

# Choisir dynamiquement le type d'isolation
insulation_types = DB.FilteredElementCollector(doc).OfClass(DB.PipeInsulationType).ToElements()
type_choices = {i.Name: i.Id for i in insulation_types if i is not None}

selected_type = forms.SelectFromList.show(
    sorted(type_choices.keys()),
    title="Choisissez un type d'isolant",
    multiselect=False
)

if not selected_type:
    raise Exception("Aucun type d'isolant sélectionné.")

insulation_type_id = type_choices[selected_type]

# Demander l'épaisseur
thickness_input = forms.ask_for_string(
    default="0.03",  # ≈ 9 mm
    prompt="Entrez l'épaisseur (en pieds, ex : 0.03 pour 9 mm)",
    title="Épaisseur de l'isolant"
)

if not thickness_input:
    raise Exception("Épaisseur non définie.")

thickness = float(thickness_input)

# Démarrer la transaction
t = Transaction(doc, "Ajouter isolant aux éléments sélectionnés")
t.Start()

count = 0
for ref in references:
    element = doc.GetElement(ref.ElementId)
    if not element:
        continue

    # Vérifie s'il n'a pas déjà d'isolation
    existing = PipeInsulation.GetInsulationIds(element)
    if existing and len(existing) > 0:
        continue

    try:
        PipeInsulation.Create(doc, element.Id, insulation_type_id, thickness)
        count += 1
    except Exception as e:
        print("Erreur pour l'élément {} : {}".format(element.Id, e))

t.Commit()

forms.alert("{} isolants ajoutés avec succès.".format(count), title="Résultat", ok=True)

except Exception as e:
forms.alert(“Erreur : {}”.format(e), title=“Erreur”, ok=True)
"

But i keep having a mistake:

i’ve loooked throught the documentation of Revit API but haven’t been able to find a way.
Am not yet very goodd at programing so im asking here if someone could give me pointers on why i can’t access the insulation in the Modele.

Hello,

The error you’re getting is caused by 2 things:

  1. There is no need to write DB in this line:
insulation_types = DB.FilteredElementCollector(doc).OfClass(DB.PipeInsulationType).ToElements()

because you already imported everything from DB using *, in this line in your imports:

from Autodesk.Revit.DB import *

Corrected version:

insulation_types = FilteredElementCollector(doc).OfClass(PipeInsulationType).ToElements()

Also, because you already imported Autodesk.Revit.DB namespace, there is no need to import it again from pyrevit in this line:

from pyrevit import DB
  1. This line in your imports is incorrect:
from Autodesk.Revit.DB.Plumbing import PipeInsulation

Instead, try this:

from Autodesk.Revit.DB.Plumbing import PipeInsulationType

PipeInsulationType and PipeInsulation are two different classes:
PipeInsulationType - PipeInsulationType Class
PipeInsulation - PipeInsulation Class

In this line you are using PipeInsulationType:

insulation_types = DB.FilteredElementCollector(doc).OfClass(DB.PipeInsulationType).ToElements()

so it must be the same in the imports.

DB is a so called namespace. FilteredElementCollector comes directly from it.
PipeInsulationType isn’t included directly in the DB namespace, but another one, called Autodesk.Revit.DB.Plumbing, that’s why you have to import it explicitly.

I hope this helped and let me know if you have any questions!

Bad imports = code generated by AI

1 Like

That’s also what I thought… However, I also used much more AI at the beginning and gradually started understanding more and more on my own, to the point of being able to write correct code without it. I think there is value in using AI while learning, the key is to only use it as one of the learning resources and not rely too much on it

2 Likes

Not always true, but ChatGPT free is good at messing up !

Thank you for the tips, it was really helpfull. As i said in the topic am only just learning now and there are many things i can’t make sense of yet.

am trying to learn by understanding why the script doesn’t work. but i seem to be stopped at every function. As Jean Marc said chatGPT is not very good at it. even the paid version is good at messing up.

You may want to try Cursor as a code editor, it is better than github copilot in vscode.

Setup your autocomplete properly and take the time to read and understand each issue.

I suggest you take @ErikFrits courses https://learnrevitapi.com/ That will get you started much faster than on your own.

2 Likes

Create a code that can read a CSV file. In this CSV file, list the pipe type, the system, the pipe size, and the necessary insulation you want to assign.

update: i managed to create it in the end after quite a bit of trial and error but here’s the final UI; if someone is interested in the script too i can post it.

here is something i had a while back ago. it’s a bit “prompty” but that was by design at the time.
takes care of both Duct and Pipe.

I’d be curious to know how you went about yours and see if there’s something you have that I don’t.

# coding: utf-8
__title__ = 'Insulate by System Type + Fittings'
__author__ = 'Rafael Martinez'
__doc__ = 'Select scope, multiple system types, insulation type & thickness, and apply/update insulation on ducts/pipes including fittings/accessories.'

import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Mechanical import Duct, DuctInsulation, DuctInsulationType
from Autodesk.Revit.DB.Plumbing import Pipe, PipeInsulation, PipeInsulationType
from Autodesk.Revit.UI import TaskDialog
from pyrevit import revit, forms

doc = revit.doc
active_view = doc.ActiveView

# --- Step 0: Choose Scope ---
scope_choice = forms.SelectFromList.show(
    ['Active View', 'Entire Project'],
    title='Select Scope',
    multiselect=False
)
if not scope_choice:
    TaskDialog.Show('Cancelled', 'Operation cancelled.')
    raise Exception('Cancelled')

# --- Step 1: Choose Duct or Pipe ---
type_choice = forms.SelectFromList.show(
    ['Duct', 'Pipe'],
    title='Select Element Type',
    multiselect=False
)
if not type_choice:
    TaskDialog.Show('Cancelled', 'Operation cancelled.')
    raise Exception('Cancelled')

# --- Step 2: Choose Apply Mode ---
apply_mode = forms.SelectFromList.show(
    ['Apply to New Only', 'Apply/Update All'],
    title='Select Apply Mode',
    multiselect=False
)
if not apply_mode:
    TaskDialog.Show('Cancelled', 'Operation cancelled.')
    raise Exception('Cancelled')

# --- Step 3: Collect Elements (incl. Fittings + Accessories) ---
elements = []

if type_choice == 'Duct':
    base_class = Duct
    ins_type_class = DuctInsulationType
    ins_func = DuctInsulation.Create
    cat_fitting = BuiltInCategory.OST_DuctFitting
    cat_accessory = BuiltInCategory.OST_DuctAccessory
else:
    base_class = Pipe
    ins_type_class = PipeInsulationType
    ins_func = PipeInsulation.Create
    cat_fitting = BuiltInCategory.OST_PipeFitting
    cat_accessory = BuiltInCategory.OST_PipeAccessory

if scope_choice == 'Active View':
    base_collector = FilteredElementCollector(doc, active_view.Id)
else:
    base_collector = FilteredElementCollector(doc)

elements += base_collector.OfClass(base_class).WhereElementIsNotElementType().ToElements()

if scope_choice == 'Active View':
    collector_fitting = FilteredElementCollector(doc, active_view.Id).OfCategory(cat_fitting)
    collector_accessory = FilteredElementCollector(doc, active_view.Id).OfCategory(cat_accessory)
else:
    collector_fitting = FilteredElementCollector(doc).OfCategory(cat_fitting)
    collector_accessory = FilteredElementCollector(doc).OfCategory(cat_accessory)

elements += collector_fitting.WhereElementIsNotElementType().ToElements()
elements += collector_accessory.WhereElementIsNotElementType().ToElements()

# --- Step 4: Collect System Types ---
system_types = set()
for el in elements:
    param = el.LookupParameter('System Type')
    if param and param.AsValueString():
        system_types.add(param.AsValueString())

if not system_types:
    TaskDialog.Show('No Systems Found', 'No system types found in chosen scope for ' + type_choice)
    raise Exception('No system types found')

selected_systems = forms.SelectFromList.show(
    sorted(system_types),
    title='Select System Type(s)',
    multiselect=True
)
if not selected_systems:
    raise Exception('Cancelled')

# --- Step 5: Filter Matching Elements ---
matched = [
    el for el in elements
    if el.LookupParameter('System Type')
    and el.LookupParameter('System Type').AsValueString() in selected_systems
]

if not matched:
    TaskDialog.Show('No Matches', 'No elements found with chosen system types.')
    raise Exception('No matches')

# --- Step 6: Insulation Type ---
ins_types = FilteredElementCollector(doc).OfClass(ins_type_class).ToElements()
ins_dict = {}
for it in ins_types:
    p = it.LookupParameter("Type Name")
    if p:
        ins_dict[p.AsString()] = it.Id

if not ins_dict:
    TaskDialog.Show('No Insulation Types', 'No insulation types found in model.')
    raise Exception('No insulation types')

selected_ins = forms.SelectFromList.show(
    sorted(ins_dict.keys()),
    title='Select Insulation Type',
    multiselect=False
)
if not selected_ins:
    raise Exception('Cancelled')

ins_id = ins_dict[selected_ins]

# --- Step 7: Thickness ---
thickness_str = forms.ask_for_string(default='2.0', prompt='Enter insulation thickness in inches:', title='Insulation Thickness')
if not thickness_str:
    raise Exception('No thickness entered')

try:
    thickness = float(thickness_str) / 12.0  # convert inches to feet
except:
    TaskDialog.Show('Invalid', 'Thickness must be a number.')
    raise Exception('Invalid thickness')

# --- Step 8: Apply or Update Insulation ---
with revit.Transaction('Apply Insulation'):
    applied = 0
    updated = 0
    skipped = 0

    for el in matched:
        try:
            existing_ids = InsulationLiningBase.GetInsulationIds(doc, el.Id)
            if existing_ids:
                if apply_mode == 'Apply/Update All':
                    for eid in existing_ids:
                        ins_el = doc.GetElement(eid)
                        ins_el.Thickness = thickness
                        ins_el.ChangeTypeId(ins_id)
                        updated += 1
                else:
                    skipped += 1
            else:
                ins_func(doc, el.Id, ins_id, thickness)
                applied += 1
        except Exception as e:
            print('Failed on {}: {}'.format(el.Id, e))

# --- Step 9: Report ---
msg = "Mode: {}\nScope: {}\n".format(apply_mode, scope_choice)
msg += "System Types: {}\n".format(", ".join(selected_systems))
msg += "Applied new insulation: {}\n".format(applied)
msg += "Updated existing insulation: {}\n".format(updated)
msg += "Skipped (already insulated): {}\n".format(skipped)

TaskDialog.Show('Complete', msg)