How do I define a family parameter in RevitAPI 2024 using python block in DesignScript?

I am creating this thread because in 2024 this seems to be done quite differently. I’ve made several attempts and there seems to be some blind spots in my knowledge about the new API. I’ve read the API changes and the Revit API docs site. Here is where I stand now and highlighted are the questions.

In my project, I want to add to a family named “NK” an instance parameter in parameter group “Other” of type “Text”. This is family loaded in my project.


Because of this formu limitations (one image and one link per post) the rest of the question follows in comments…

Manually (from GUI) I can do that and I do see the result:

So I tried to create a script for it:

The python code:

import sys
import clr
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

## Input parameters
familyName = IN[0]
family = UnwrapElement(IN[1])


## Test if family already contains the parameter
def familyHasParameter(family, parameterName):
        currentParams = family.FamilyManager.GetParameters()
        for k in range(len(currentParams)):
                if currentParams[k].Definition.Name == parameterName:
                        return True
        return False


## Get Family document from family instance
def getFamilyDoc(fam, doc):
        if isinstance(fam, Autodesk.Revit.DB.FamilyInstance):
                return (Document.EditFamily(doc, fam.Symbol.Family))
        else:
                if fam.IsInPlace == False:
                        return (Document.EditFamily(doc, fam))


## Implementation of family load options - always override existing family
class FamilyLoadOptions(IFamilyLoadOptions):
        def     OnFamilyFound(self, familyInUse, overwriteParameterValues):
                overwriteParameterValues = True
                return True

        def     OnSharedFamilyFound(self, sharedFamily, familyInUse, source, overwriteParameterValues):
                source = FamilySource.Family
                overwriteParameterValues = True
                return True


###########################################################


TransactionManager.Instance.TransactionTaskDone()
TransactionManager.Instance.ForceCloseTransaction()

doc = DocumentManager.Instance.CurrentDBDocument


print("-- Editing family: " + familyName)
familyDoc = getFamilyDoc(family, doc)


TransactionManager.Instance.EnsureInTransaction(doc)
trans = Transaction(familyDoc, "Edit family params")
trans.Start()


parameterName = "TEST"
groupTypeId = Autodesk.Revit.DB.GroupTypeId.Data
specTypeId = Autodesk.Revit.DB.SpecTypeId.Current
isInstance = True

if familyHasParameter(familyDoc, parameterName):
    print(("-- Parameter already exists on family: " + parameterName))
else:
    print(("-- Adding parameter to family: " + parameterName))
    familyDoc.FamilyManager.AddParameter(parameterName, groupTypeId, specTypeId, isInstance)
    
trans.Commit()
trans.Dispose()

TransactionManager.Instance.TransactionTaskDone()
TransactionManager.Instance.ForceCloseTransaction()

print("-- Saving family to project")
familyDoc.LoadFamily(doc, FamilyLoadOptions())
    
TransactionManager.Instance.TransactionTaskDone()
TransactionManager.Instance.ForceCloseTransaction()

And after all the attempts I get following error (calling doc.LoadFamily this way worked in 2022):
error load

Following are my concerns and questions:

  • There is no Autodesk.Revit.DB.GroupTypeId.Other, I am using “Data” for now, but it is odd I don’t have the same optios as in GUI.
  • There is no Autodesk.Revit.DB.SpecTypeId.Text, I am using “Current” just to see some result, but obviously that is not, what I want. I also tried using SpectypeId.Custom, which failde with something like “Not a spec type” error.
  • The error above about LoadFamily not accepting the second argument. If I ommit the second argument I am unable to force override to the family in the project, that I am actually trying to override.

I would be greatful for any hint to get me unstuck, because I already have several hours into this matter and do not see the light yet :pray: Thanks in advance.

SpecType is string. (autodesk.spec:spec.string-2.0.0)
You are going to see a bunch of new string types in Revit based on what SpecType supports. So more than just text in the future I expect. Note autodesk.spec.string:url-2.0.0 and autodesk.spec.aec:multilineText-2.0.0 i’m guessing we’ll see more formatting options in the future.

ParameterGroup “Other” is just INVALID. INVALID has no label.
ParameterGroup Property (revitapidocs.com)

An note that INVALID doesn’t have a TypeId:

image

Thank you for your response. Now I know more about how the IDs work, but I still struggle to find connection between the API and GUI ways of doing the operation of adding parameters.

SpecType

So when SpecType is an id string (like this one: “autodesk.spec:spec.string-2.0.0”) and since SpecTypeId enum does not contain anything like “Text” or “String” or “MultilineText”, where do I get list of actual valid and usable SpecTypes. I just want to use the same ones as in the GUI drop down named “Data Type” show in the dialog for creating Family Instance property in Revit (see above on the first image of this question). Where do I get those? Where did you get the ones you mention in the response?

ParameterGroup

From the blog post mentioned above I noticed this API change. From that I assume a shift from BuiltInParameterGroup to ForgeTypeId so I wonder if that makes BuiltInParameterGroup obsolete in the future…? I will try to use the INVALID group for that.

FamilyManager.AddParameter(string, BuiltInParameterGroup, Category, bool) → FamilyManager.AddParameter(String, ForgeTypeId, Category, bool)

LoadFamily

There seems to be API change in this function that makes it completely broken in Revit API 2024.

Again, thanks for the extra info.

SpecTypes are listed in the API reference.
SpecTypeId Class (revitapidocs.com)
You have Revit Lookup installed? Yes?

For now, BuiltInParameterGroup is valid. ForgeTypeId is preferred. I would indeed expect BuiltInParameterGroups to go away down the road. Deprecated in 2024

1.4.2. FamilyManager

Deprecated member(s) → Replacement member(s) FamilyManager.AddParameter(ExternalDefinition, BuiltInParameterGroup, bool) → FamilyManager.AddParameter(ExternalDefiniti on, ForgeTypeId, bool) FamilyManager.AddParameter(string, BuiltInParameterGroup, Category, bool) → FamilyManager.AddParameter(String, ForgeTypeId, Category, bool) FamilyManager.ReplaceParameter(FamilyParameter, ExternalDefinition, BuiltInParameterGroup, bool) → FamilyManager.ReplaceParameter(FamilyPara meter, ExternalDefinition, ForgeTypeId, bool) FamilyManager.ReplaceParameter(FamilyParameter, string, BuiltInParameterGroup, bool) → FamilyManager.ReplaceParameter(FamilyPara meter, ExternalDefinition, ForgeTypeId, bool) FamilyManager.IsUserAssignableParameterGroup(BuiltInParameterGroup) → FamilyManager.IsUserAssignableParameterGroup(ForgeTypeId)

Yes. That is the very docs link I mentioned in my last response. The constants you mentioned are’t listed there. The SpecTypeId doesn’t hold anything like String, Text, Multiline - just Ctrl+F the docs. It is not constant of that class, yet in the dropdown in the GUI I do have the option. So where is it in the API? That’s the mistery.

No, I wasn’t avare of the Revit Lookup tool and it looks like my next favourite thing.

Yes. That is the API changes I also see in the post I mentioned. As written in the topic of this forum, the question indeed is about API v2024, so the BuiltInParameterGroup is already deprecated for me and I’m trying to find its equivalent using ForgeTypeId.

You have this document?

Autodesk ForgeType.pdf (284.8 KB)

Multiline and URL are properties of SpecTypeId.String.

SpecTypeId.String, SpecTypeId.Reference, SpecTypeId.Int and SpecTypeId.Boolean are all defined as classes underSpecTypeId. All the others are listed as properties. (Go figure) Seems it would have been simpler just to make a Text, Multiline and URL property. Boolean only has one property(?) So are we going to get True/False and 0,1 properties. Of course we could really use a True/False/Null property - but then is it really a Boolean.

image

Revit Lookup will let you see the ForgeTypeId strings. You can always use those directly. I’ve been doing that as I think the whole SpecTypeId and GroupTypeId is still getting sorted out.

1 Like

No I wasn’t aware of that document. Where would I get it? Isn’t there by any change similar document on changes of document.LoadFamily(familyOptions, saveOptions) API ;D?

Big thanks! I totally missed those sub-classes. I was only searching within the properties as I expected the list would be thorough. I agree 100% that the separation into subclases complicates it a bit and don’t really help.

Also I didn’t notice the subclasses listed under code-completion in the Dynamo Python block editor, which I find odd. Is it possible that such a subclass would translate from the (I suppose) original .NET implementation to Python like something else, perhaps something like SpecTypeId_String? I know getters and setters do this.

When I get to Revit and Revit Lookup I will too try to dig down into it. Thanx

Allright. So I sat to the code doc.LoadFamily is totally not accepting save options in 2024 but won’t let me override existing family otherwise so I’m stuck.

As for what @aaronrumple suggested SpecTypeId.String.Text works in python. I din’t figure out the way to use Data group though. Where would I pass the id string for the group? I tried using ForgeTypeId("") as a consturctor, but that threw an exception as I apparently should only use the predefined constants, but there is no such constants equivalent to the formal BuiltInParameterGroup.INVALID, so I’m stuck again. I ended up (ab)using the GroupTypeId.Ifc instead as it just is there and feels the least wrong, but that seems far from correct to me =/

I’m trying to get the Revit Lookup and RevitPythonSheel through our company validation. It takes time.

I don’t get how are we supposed to work with Dynamo/Revit 2024 when the api docs arent out and the present api seems to be missing stuff. Am I missing something?

In case someone get’s here searching for the same error familyDoc.LoadFamily(doc, FamilyLoadOptions()) throwing TypeError: interface takes exactly one argument in Revit 2024 - it is most likely caused by CPython3.

This isn’t an issue with CPython, but more likely with Python 3 entirely as IronPython3 fails similarly. The reason this fails now is that classes required a namespace to pass to the .net environment cleanly. If you provide a namespace things should work without issue for the first run.

However namespaces cannot be duplicated in the same kernal, and as the class with it’s namespace has been passed once already it either needs to be disposed after run (haven’t tested this as it isn’t something which I have needed in my work of late) or a unique namespace needs to be generated for each run (works without issue for a few hundred runs while developing code anyway).

A non-Revit centered version of the problem and the same solution can be found here: Implement a C# Interface in Python for .NET - Stack Overflow

1 Like

Not installed Revit 2024 yet, but I just quickly tested an implementation of a IFamilyLoadOptions class on Revit Preview works well with IronPython3.
Even with the uuid trick, I have a crash with CPython3 (try to debug later)

1 Like

Can you post the code you used here @c.poupin?

Sure

the code

  • get all doors in project,
  • edit each family,
    - set the parameter ROOM_CALCULATION_POINT
    - reload family in Project
import clr
import sys
import System
from System.Collections.Generic import List
### -------------  Import RevitAPI -----------------------------------------###
clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB

### -------------  Import DocumentManager y TransactionManager -------------###
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from System.Collections.Generic import *

### ------------- Here all Definitions --------------------------------------###
# tolist() function to ensure that the element is iterable
def tolist(x):
	if hasattr(x,'__iter__'): return x
	else : return [x]

### ------------- Current document and User Interface. ----------------------###
doc =  DocumentManager.Instance.CurrentDBDocument

##class for Load ##		
class FamilyOption(IFamilyLoadOptions) :
	def OnFamilyFound(self, familyInUse, overwriteParameterValues):
		overwriteParameterValues.Value = True
		return True

	def OnSharedFamilyFound(self, sharedFamily, familyInUse, source, overwriteParameterValues):
		overwriteParameterValues.Value = True
		return True	

# Retrieve all the door families in the project
filterDoors = System.Predicate[System.Object](lambda x :x.FamilyCategory.Id == ElementId(BuiltInCategory.OST_Doors))
doorFamilies = List[DB.Element](FilteredElementCollector(doc).OfClass(Family).ToElements()).FindAll(filterDoors)

TransactionManager.Instance.ForceCloseTransaction()

# Loop through each door family
for family in doorFamilies:
	# Open the door family for editing
	familyDoc = doc.EditFamily(family)
	owner_family = familyDoc.OwnerFamily
	TransactionManager.Instance.ForceCloseTransaction()
	TransactionManager.Instance.EnsureInTransaction(familyDoc)
	# Find the parameter you want to change
	parameter = owner_family.get_Parameter(BuiltInParameter.ROOM_CALCULATION_POINT)
	# Update the parameter value
	parameter.Set(1)
	# Load the updated door family back into the project and overwrite the existing version and its parameters
	TransactionManager.Instance.TransactionTaskDone()
	TransactionManager.Instance.ForceCloseTransaction()
	# load to Project and close
	familyDoc.LoadFamily(doc, FamilyOption())
	familyDoc.Close(False)
2 Likes

Your collection method won’t work in CPython, so it’s going to take a bit more time to review and build a foolproof value than I currently have… May have to come back to this on Monday.

Easily fix for this problem (I edit my previous code), but it’s not the main problem, you can re-try it

moreover normally version 3 of PythonNet no longer does auto-conversion to python objects, so in the future a Net Collection should remain a Net Collection (with the CPython engine)

This is working for multiple uses including after edit - save - dynamo run - edit - save - dynamo run workflows. However when I hit Run in the Python editor that is causing a crash on subsequent edits, which appears to be a regression in 2024 as 2023.1 works fine… :sob:

Before running Script with Cpython3 engine, Doors are ROOM_CALCULATION_POINT disable in project ?