Dear Grubdex,
Thank you for your patience.
The development team replied:
In the customer's RVT file, they are placing the Lighting Switches / Two Way Switch family. They noticed that when placing this family via the API, the "Schedule Level" instance parameter is present but blank, whereas when the family is placed via the UI the "Schedule Level" Instance parameter is set to the host level of the host element. (So, levelId 30 for a wall on Level 1.)
I tried placing the switch via the UI. I chose Placement / Place on Face in the placement editor ribbon. When clicking on a face of a wall, I can see the "Schedule Level" ID getting set from the FamilyInstance constructor.
When placing via the API, NewFamilyInstance(Face, XYZ, XYZ, FamilySymbol) is the overload that lets me place the switch on the face at a particular location/height.
However, the FamilyInstance constructor's callstack does not pass along the valid Level ID to be set in the new family instance.
The Schedule Level is left blank.
Using a modified version of Grubdex's sample code, I was able to place the "Lighting Switches / Two Way Switch" family on levels and walls in HOTEL_2.rvt. I tested in Revit development version and in Revit 2023.
I had to manually place one "Two Way Switch" before the API could place additional switches, too.
I can confirm that the "Schedule Level" parameter is left empty (no level selected) when using the NewFamilyInstance APIs that Grubdex tried in the sample code. However, all new family instances I created that had "Schedule Level" parameter editable in the UI could also be edited by the API. To set "Schedule Level" via the API, I just needed to get/set parameter "BuiltInParameter.INSTANCE_SCHEDULE_ONLY_LEVEL_PARAM" with a valid level ID.
newFamilyInstance.get_Parameter(BuiltInParameter.INSTANCE_SCHEDULE_ONLY_LEVEL_PARAM).Set(level2id);
In the forum post from Grubdex there was some questioning about which NewFamilyInstance API was the right for placing switches in walls. I believe that NewFamilyInstance Method (Face, XYZ, XYZ, FamilySymbol) makes the most sense in this context, as the Two Way Switch family is face-based. To get the wall face that would host the switch, I used wall.get_Geometry() like this:
List<Face> faces = new List<Face>();
Options geomOptions = new Options();
geomOptions.ComputeReferences = true;
GeometryElement wallGeom = wall.get_Geometry(geomOptions);
foreach (GeometryObject geomObj in wallGeom)
{
Solid geomSolid = geomObj as Solid;
if (null != geomSolid)
{
foreach (Face geomFace in geomSolid.Faces)
{
faces.Add(geomFace);
}
break;
}
}
var faceRef = doc.Create.NewFamilyInstance(faces[0], pos, orientation, switchFamilySymbol);
// Set the "Schedule Level" parameter to Level 2
faceRef.get_Parameter(BuiltInParameter.INSTANCE_SCHEDULE_ONLY_LEVEL_PARAM).Set(level2id);
I did not have success using HostObjectUtils.GetSideFaces() to get the wall face.
Here is my C# macro which places a Two Way Switch in a wall from HOTEL_2.rvt. With Revit 2023, you can open the attached hotel_2_macro_r2023.rvt to play with it.
public void PlaceSwitchAndUpdateScheduleLevel()
{
Document doc = Document;
//these params are hardcoded for quick testing
FamilySymbol switchFamilySymbol = doc.GetElement(new ElementId(361776)) as FamilySymbol;
Wall wall = doc.GetElement(new ElementId(353023)) as Wall;;
ElementId level2id = new ElementId(9946);
var pos = new XYZ(-14.0, 4.925404783, 3.937007874);
var orientation = new XYZ(-1, 0, 0);
// end hardcoded
using( var trans = new Transaction( doc ) )
{
trans.Start( "test placement" );
List<Face> faces = new List<Face>();
Options geomOptions = new Options();
geomOptions.ComputeReferences = true;
GeometryElement wallGeom = wall.get_Geometry(geomOptions);
foreach (GeometryObject geomObj in wallGeom)
{
Solid geomSolid = geomObj as Solid;
if (null != geomSolid)
{
foreach (Face geomFace in geomSolid.Faces)
{
faces.Add(geomFace);
}
break;
}
}
var faceRef = doc.Create.NewFamilyInstance(faces[0], pos, orientation, switchFamilySymbol);
// Set the "Schedule Level" parameter to Level 2
faceRef.get_Parameter(BuiltInParameter.INSTANCE_SCHEDULE_ONLY_LEVEL_PARAM).Set(level2id);
trans.Commit();
}
}
Please see the responses above regarding placing a light switch family on a wall face and setting the "Schedule Level" parameter. I only focused on the simple goal of placing a switch on a wall, and did not dig into the other "tricky issues."
I hope this helps.
Best regards,
Jeremy