From 7c4e02ee3f241401f3230141b1dbae369d3b13bf Mon Sep 17 00:00:00 2001
From: Mark Wolfman <canismarko@gmail.com>
Date: Tue, 14 Nov 2023 08:43:28 -0600
Subject: [PATCH 1/9] Another failed attempt to fix the CI seg faults.

---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4404bc4f..cb22f9c5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -45,4 +45,4 @@ jobs:
         # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
     - name: Test with pytest
-      run: pytest --timeout=120
+      run: pytest --timeout=120  src/firefly/tests/test_run_browser.py src/firefly/tests/test_voltmeters.py src/firefly/tests/test_xrf_detector_display.py

From dec60712767d59916a49637648ba77505eabb1df Mon Sep 17 00:00:00 2001
From: Mark Wolfman <canismarko@gmail.com>
Date: Tue, 14 Nov 2023 16:21:35 -0600
Subject: [PATCH 2/9] Replaced ophyd plugin with the equivalent feature from
 Typhos.

---
 .github/workflows/ci.yml                      |   2 +-
 environment.yml                               |   1 +
 src/conftest.py                               |  21 +-
 src/firefly/application.py                    |   1 -
 src/firefly/area_detector_overlay.ui          |  12 +-
 src/firefly/area_detector_roi.ui              |  16 +-
 src/firefly/area_detector_viewer.ui           |  16 +-
 src/firefly/bss.ui                            |  30 +-
 src/firefly/camera.ui                         |   2 +-
 src/firefly/energy.ui                         |   8 +-
 src/firefly/ion_chamber.ui                    |  54 +-
 src/firefly/launcher.py                       |   6 -
 src/firefly/motor.ui                          |  30 +-
 src/firefly/ophyd_plugin.py                   | 175 ------
 src/firefly/status.ui                         |  26 +-
 src/firefly/tests/test_ophyd_connection.py    | 167 ++---
 .../tests/test_xrf_detector_display.py        |   2 +-
 src/firefly/voltmeter.ui                      |  18 +-
 src/firefly/xrf_detector.py                   |  10 +-
 src/firefly/xrf_detector.ui                   |  18 +-
 src/firefly/xrf_roi.ui                        |  10 +-
 tests/conftest.py                             | 584 +++++++++---------
 22 files changed, 518 insertions(+), 691 deletions(-)
 delete mode 100644 src/firefly/ophyd_plugin.py

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cb22f9c5..4404bc4f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -45,4 +45,4 @@ jobs:
         # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
     - name: Test with pytest
-      run: pytest --timeout=120  src/firefly/tests/test_run_browser.py src/firefly/tests/test_voltmeters.py src/firefly/tests/test_xrf_detector_display.py
+      run: pytest --timeout=120
diff --git a/environment.yml b/environment.yml
index f2441cfc..bb35fa30 100644
--- a/environment.yml
+++ b/environment.yml
@@ -90,6 +90,7 @@ dependencies:
   - ophyd >=1.6.3
   - pcdsdevices  # For extra signal types
   - pydm >=1.18.0
+  - typhos
   - p4p
   - tiled-server
   - tiled-client >= 0.1.0a106  # 2023-10-02 to get new Cache() behavior
diff --git a/src/conftest.py b/src/conftest.py
index 1ffdd468..81b81577 100644
--- a/src/conftest.py
+++ b/src/conftest.py
@@ -13,7 +13,8 @@
     fake_device_cache,
     FakeEpicsSignal,
 )
-from pydm.data_plugins import add_plugin
+# from pydm.data_plugins import plugin_modules, add_plugin
+import pydm
 from pytestqt.qt_compat import qt_api
 
 import haven
@@ -28,7 +29,6 @@
 from haven.instrument.xspress import Xspress3Detector, add_mcas as add_xspress_mcas
 from firefly.application import FireflyApplication
 from firefly.main_window import FireflyMainWindow
-from firefly.ophyd_plugin import OphydPlugin
 
 
 top_dir = Path(__file__).parent.resolve()
@@ -124,16 +124,21 @@ def sim_registry(monkeypatch):
     monkeypatch.setattr(
         haven.instrument.ion_chamber, "caget", mock.AsyncMock(return_value="I0")
     )
-    # Clean the registry so we can restore it later
+    # Save the registry so we can restore it later
     registry = haven.registry
+    use_typhos = registry.use_typhos
     objects_by_name = registry._objects_by_name
     objects_by_label = registry._objects_by_label
     registry.clear()
     # Run the test
-    yield registry
-    # Restore the previous registry components
-    registry._objects_by_name = objects_by_name
-    registry._objects_by_label = objects_by_label
+    try:
+        yield registry
+    finally:
+        # Restore the previous registry components
+        registry.clear(clear_typhos=True)
+        registry._objects_by_name = objects_by_name
+        registry._objects_by_label = objects_by_label
+        registry.use_typhos = use_typhos
 
 
 @pytest.fixture()
@@ -265,7 +270,7 @@ def shutters(sim_registry):
 
 @pytest.fixture(scope="session")
 def pydm_ophyd_plugin():
-    return add_plugin(OphydPlugin)
+    return pydm.data_plugins.plugin_for_address("sig://")
 
 
 qs_status = {
diff --git a/src/firefly/application.py b/src/firefly/application.py
index f775d72c..c61442cd 100644
--- a/src/firefly/application.py
+++ b/src/firefly/application.py
@@ -23,7 +23,6 @@
 from haven import HavenMotor, registry, load_config
 import haven
 from .main_window import FireflyMainWindow, PlanMainWindow
-from .ophyd_plugin import OphydPlugin
 from .queue_client import QueueClient, QueueClientThread, queueserver_api
 
 generator = type((x for x in []))
diff --git a/src/firefly/area_detector_overlay.ui b/src/firefly/area_detector_overlay.ui
index 1ac415b2..450053de 100644
--- a/src/firefly/area_detector_overlay.ui
+++ b/src/firefly/area_detector_overlay.ui
@@ -44,7 +44,7 @@
         <string/>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.overlays.overlay_${OV}.use</string>
+        <string>sig://${AD}.overlays.overlay_${OV}.use</string>
        </property>
       </widget>
      </item>
@@ -61,7 +61,7 @@
         <string/>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.overlays.overlay_${OV}.shape</string>
+        <string>sig://${AD}.overlays.overlay_${OV}.shape</string>
        </property>
       </widget>
      </item>
@@ -75,7 +75,7 @@
         <string/>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.overlays.overlay_${OV}.position_y</string>
+        <string>sig://${AD}.overlays.overlay_${OV}.position_y</string>
        </property>
        <property name="orientation" stdset="0">
         <enum>Qt::Vertical</enum>
@@ -100,7 +100,7 @@
           <string/>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${AD}.overlays.overlay_${OV}.position_x</string>
+          <string>sig://${AD}.overlays.overlay_${OV}.position_x</string>
          </property>
          <property name="step_size" stdset="0">
           <number>0</number>
@@ -132,7 +132,7 @@
         <string/>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.overlays.overlay_${OV}.size_y</string>
+        <string>sig://${AD}.overlays.overlay_${OV}.size_y</string>
        </property>
        <property name="orientation" stdset="0">
         <enum>Qt::Vertical</enum>
@@ -157,7 +157,7 @@
           <string/>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${AD}.overlays.overlay_${OV}.size_x</string>
+          <string>sig://${AD}.overlays.overlay_${OV}.size_x</string>
          </property>
         </widget>
        </item>
diff --git a/src/firefly/area_detector_roi.ui b/src/firefly/area_detector_roi.ui
index 477048f9..568bdce5 100644
--- a/src/firefly/area_detector_roi.ui
+++ b/src/firefly/area_detector_roi.ui
@@ -50,7 +50,7 @@
         <string>NaN</string>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.stats${R}.mean_value</string>
+        <string>sig://${AD}.stats${R}.mean_value</string>
        </property>
       </widget>
      </item>
@@ -77,7 +77,7 @@
         <string>NaN</string>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.stats${R}.min_value</string>
+        <string>sig://${AD}.stats${R}.min_value</string>
        </property>
       </widget>
      </item>
@@ -104,7 +104,7 @@
         <string>NaN</string>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.stats${R}.max_value</string>
+        <string>sig://${AD}.stats${R}.max_value</string>
        </property>
       </widget>
      </item>
@@ -132,7 +132,7 @@
       <string>Show</string>
      </property>
      <property name="channel" stdset="0">
-      <string>oph://${AD}.overlays.overlay_${R}.use</string>
+      <string>sig://${AD}.overlays.overlay_${R}.use</string>
      </property>
     </widget>
    </item>
@@ -144,7 +144,7 @@
         <string/>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.roi${R}.min_xyz.min_y</string>
+        <string>sig://${AD}.roi${R}.min_xyz.min_y</string>
        </property>
        <property name="orientation" stdset="0">
         <enum>Qt::Vertical</enum>
@@ -169,7 +169,7 @@
           <string/>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${AD}.roi${R}.min_xyz.min_x</string>
+          <string>sig://${AD}.roi${R}.min_xyz.min_x</string>
          </property>
          <property name="step_size" stdset="0">
           <number>0</number>
@@ -201,7 +201,7 @@
         <string/>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${AD}.roi${R}.size.y</string>
+        <string>sig://${AD}.roi${R}.size.y</string>
        </property>
        <property name="orientation" stdset="0">
         <enum>Qt::Vertical</enum>
@@ -226,7 +226,7 @@
           <string/>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${AD}.roi${R}.size.x</string>
+          <string>sig://${AD}.roi${R}.size.x</string>
          </property>
         </widget>
        </item>
diff --git a/src/firefly/area_detector_viewer.ui b/src/firefly/area_detector_viewer.ui
index 675c9fe9..07398350 100644
--- a/src/firefly/area_detector_viewer.ui
+++ b/src/firefly/area_detector_viewer.ui
@@ -130,7 +130,7 @@
                   <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
                  </property>
                  <property name="channel" stdset="0">
-                  <string>oph://${AD}.cam.array_size.array_size_y</string>
+                  <string>sig://${AD}.cam.array_size.array_size_y</string>
                  </property>
                 </widget>
                </item>
@@ -153,7 +153,7 @@
                   <string>1224</string>
                  </property>
                  <property name="channel" stdset="0">
-                  <string>oph://${AD}.cam.array_size.array_size_x</string>
+                  <string>sig://${AD}.cam.array_size.array_size_x</string>
                  </property>
                 </widget>
                </item>
@@ -206,7 +206,7 @@
                   <string>[]</string>
                  </property>
                  <property name="channel" stdset="0">
-                  <string>oph://${AD}.cam.gain</string>
+                  <string>sig://${AD}.cam.gain</string>
                  </property>
                  <property name="tickPosition" stdset="0">
                   <enum>QSlider::TicksBelow</enum>
@@ -234,7 +234,7 @@
                   <string>Auto</string>
                  </property>
                  <property name="channel" stdset="0">
-                  <string>oph://${AD}.cam.gain_auto</string>
+                  <string>sig://${AD}.cam.gain_auto</string>
                  </property>
                  <property name="pressValue" stdset="0">
                   <string>1</string>
@@ -257,7 +257,7 @@
                   <string>[]</string>
                  </property>
                  <property name="channel" stdset="0">
-                  <string>oph://${AD}.cam.acquire_time</string>
+                  <string>sig://${AD}.cam.acquire_time</string>
                  </property>
                  <property name="tickPosition" stdset="0">
                   <enum>QSlider::TicksBelow</enum>
@@ -282,7 +282,7 @@
                   <string>Auto</string>
                  </property>
                  <property name="channel" stdset="0">
-                  <string>oph://${AD}.cam.acquire_time_auto</string>
+                  <string>sig://${AD}.cam.acquire_time_auto</string>
                  </property>
                  <property name="pressValue" stdset="0">
                   <string>1</string>
@@ -313,7 +313,7 @@
                 <bool>false</bool>
                </property>
                <property name="channel" stdset="0">
-                <string>oph://${AD}.cam.acquire</string>
+                <string>sig://${AD}.cam.acquire</string>
                </property>
                <property name="passwordProtected" stdset="0">
                 <bool>false</bool>
@@ -356,7 +356,7 @@
                 <string>[{&quot;name&quot;: &quot;enable_acquire_buttons&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;&quot;, &quot;expression&quot;: &quot;ch[0] == 1&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;${PREFIX}cam1:Acquire&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
                </property>
                <property name="channel" stdset="0">
-                <string>oph://${AD}.cam.acquire</string>
+                <string>sig://${AD}.cam.acquire</string>
                </property>
                <property name="pressValue" stdset="0">
                 <string>0</string>
diff --git a/src/firefly/bss.ui b/src/firefly/bss.ui
index 7dfaf67c..a990b370 100644
--- a/src/firefly/bss.ui
+++ b/src/firefly/bss.ui
@@ -88,7 +88,7 @@
            <string>688743</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_proposal_proposal_id</string>
+           <string>sig://bss_proposal_proposal_id</string>
           </property>
          </widget>
         </item>
@@ -105,7 +105,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_proposal_title</string>
+           <string>sig://bss_proposal_title</string>
           </property>
           <property name="displayFormat" stdset="0">
            <enum>PyDMLineEdit::String</enum>
@@ -127,7 +127,7 @@
              <string/>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://bss_proposal_start_date</string>
+             <string>sig://bss_proposal_start_date</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLineEdit::String</enum>
@@ -147,7 +147,7 @@
              <string/>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://bss_proposal_end_date</string>
+             <string>sig://bss_proposal_end_date</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLineEdit::String</enum>
@@ -169,7 +169,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_proposal_user_last_names</string>
+           <string>sig://bss_proposal_user_last_names</string>
           </property>
           <property name="displayFormat" stdset="0">
            <enum>PyDMLineEdit::String</enum>
@@ -189,7 +189,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_proposal_user_badges</string>
+           <string>sig://bss_proposal_user_badges</string>
           </property>
           <property name="displayFormat" stdset="0">
            <enum>PyDMLineEdit::String</enum>
@@ -209,7 +209,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_proposal_mail_in_flag</string>
+           <string>sig://bss_proposal_mail_in_flag</string>
           </property>
          </widget>
         </item>
@@ -226,7 +226,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_proposal_proprietary_flag</string>
+           <string>sig://bss_proposal_proprietary_flag</string>
           </property>
          </widget>
         </item>
@@ -303,7 +303,7 @@
            <string>257794</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_esaf_esaf_id</string>
+           <string>sig://bss_esaf_esaf_id</string>
           </property>
          </widget>
         </item>
@@ -323,7 +323,7 @@
            <string>Approved</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_esaf_esaf_status</string>
+           <string>sig://bss_esaf_esaf_status</string>
           </property>
          </widget>
         </item>
@@ -340,7 +340,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_esaf_title</string>
+           <string>sig://bss_esaf_title</string>
           </property>
           <property name="displayFormat" stdset="0">
            <enum>PyDMLineEdit::String</enum>
@@ -362,7 +362,7 @@
              <string/>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://bss_esaf_start_date</string>
+             <string>sig://bss_esaf_start_date</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLineEdit::String</enum>
@@ -382,7 +382,7 @@
              <string/>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://bss_esaf_end_date</string>
+             <string>sig://bss_esaf_end_date</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLineEdit::String</enum>
@@ -404,7 +404,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_esaf_user_last_names</string>
+           <string>sig://bss_esaf_user_last_names</string>
           </property>
           <property name="displayFormat" stdset="0">
            <enum>PyDMLineEdit::String</enum>
@@ -424,7 +424,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_esaf_user_badges</string>
+           <string>sig://bss_esaf_user_badges</string>
           </property>
           <property name="displayFormat" stdset="0">
            <enum>PyDMLineEdit::String</enum>
diff --git a/src/firefly/camera.ui b/src/firefly/camera.ui
index 2f2c5a0f..7babc1c0 100644
--- a/src/firefly/camera.ui
+++ b/src/firefly/camera.ui
@@ -59,7 +59,7 @@
         <bool>true</bool>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${CAMERA}.cam.acquire</string>
+        <string>sig://${CAMERA}.cam.acquire</string>
        </property>
        <property name="onColor" stdset="0">
         <color>
diff --git a/src/firefly/energy.ui b/src/firefly/energy.ui
index d6321def..489d190a 100644
--- a/src/firefly/energy.ui
+++ b/src/firefly/energy.ui
@@ -216,7 +216,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://monochromator_mode</string>
+           <string>sig://monochromator_mode</string>
           </property>
          </widget>
         </item>
@@ -254,7 +254,7 @@
            <bool>false</bool>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://monochromator_energy_user_readback</string>
+           <string>sig://monochromator_energy_user_readback</string>
           </property>
          </widget>
         </item>
@@ -383,7 +383,7 @@
            <bool>true</bool>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://undulator_gap</string>
+           <string>sig://undulator_gap</string>
           </property>
          </widget>
         </item>
@@ -440,7 +440,7 @@
            <bool>true</bool>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://undulator_energy</string>
+           <string>sig://undulator_energy</string>
           </property>
          </widget>
         </item>
diff --git a/src/firefly/ion_chamber.ui b/src/firefly/ion_chamber.ui
index 96548d2c..a9ce472d 100644
--- a/src/firefly/ion_chamber.ui
+++ b/src/firefly/ion_chamber.ui
@@ -48,7 +48,7 @@
       <string>The name of this ion chamber.</string>
      </property>
      <property name="channel" stdset="0">
-      <string>oph://${IC}.description</string>
+      <string>sig://${IC}.description</string>
      </property>
      <property name="enableRichText" stdset="0">
       <bool>false</bool>
@@ -103,7 +103,7 @@
            </size>
           </property>
           <property name="rules" stdset="0">
-           <string>[{&quot;name&quot;: &quot;Gain button lower range&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0] &lt; 27&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${IC}.preamp.sensitivity_level&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;If the sensitivity is at its highest setting, disable the button so users don't click it unnecessarily.&quot;}]</string>
+           <string>[{&quot;name&quot;: &quot;Gain button lower range&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0] &lt; 27&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${IC}.preamp.sensitivity_level&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;If the sensitivity is at its highest setting, disable the button so users don't click it unnecessarily.&quot;}]</string>
           </property>
           <property name="alarmSensitiveContent" stdset="0">
            <bool>false</bool>
@@ -118,7 +118,7 @@
            <bool>false</bool>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://${IC}.preamp.sensitivity_level</string>
+           <string>sig://${IC}.preamp.sensitivity_level</string>
           </property>
           <property name="passwordProtected" stdset="0">
            <bool>false</bool>
@@ -155,7 +155,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://${IC}.preamp.sensitivity_value</string>
+           <string>sig://${IC}.preamp.sensitivity_value</string>
           </property>
          </widget>
         </item>
@@ -165,7 +165,7 @@
            <string/>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://${IC}.preamp.sensitivity_unit</string>
+           <string>sig://${IC}.preamp.sensitivity_unit</string>
           </property>
          </widget>
         </item>
@@ -196,7 +196,7 @@
            </size>
           </property>
           <property name="rules" stdset="0">
-           <string>[{&quot;name&quot;: &quot;Gain button upper range&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${IC}.preamp.sensitivity_level&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;&quot;}]</string>
+           <string>[{&quot;name&quot;: &quot;Gain button upper range&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${IC}.preamp.sensitivity_level&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;&quot;}]</string>
           </property>
           <property name="alarmSensitiveContent" stdset="0">
            <bool>false</bool>
@@ -211,7 +211,7 @@
            <bool>false</bool>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://${IC}.preamp.sensitivity_level</string>
+           <string>sig://${IC}.preamp.sensitivity_level</string>
           </property>
           <property name="passwordProtected" stdset="0">
            <bool>false</bool>
@@ -274,7 +274,7 @@
            <string>The gain of the amplifier</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://${IC}.preamp.gain_db</string>
+           <string>sig://${IC}.preamp.gain_db</string>
           </property>
           <property name="enableRichText" stdset="0">
            <bool>false</bool>
@@ -359,7 +359,7 @@
               <string/>
              </property>
              <property name="channel" stdset="0">
-              <string>oph://${IC}.preamp.offset_on</string>
+              <string>sig://${IC}.preamp.offset_on</string>
              </property>
             </widget>
            </item>
@@ -369,7 +369,7 @@
               <string/>
              </property>
              <property name="rules" stdset="0">
-              <string>[{&quot;name&quot;: &quot;Offset disabled&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;bool(ch[0])&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${IC}.preamp.offset_on&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: true}], &quot;notes&quot;: &quot;&quot;}]</string>
+              <string>[{&quot;name&quot;: &quot;Offset disabled&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;bool(ch[0])&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${IC}.preamp.offset_on&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: true}], &quot;notes&quot;: &quot;&quot;}]</string>
              </property>
              <property name="alarmSensitiveContent" stdset="0">
               <bool>false</bool>
@@ -384,7 +384,7 @@
               <bool>false</bool>
              </property>
              <property name="channel" stdset="0">
-              <string>oph://${IC}.preamp.offset_sign</string>
+              <string>sig://${IC}.preamp.offset_sign</string>
              </property>
             </widget>
            </item>
@@ -406,7 +406,7 @@
               <bool>false</bool>
              </property>
              <property name="channel" stdset="0">
-              <string>oph://${IC}.preamp.offset_value</string>
+              <string>sig://${IC}.preamp.offset_value</string>
              </property>
             </widget>
            </item>
@@ -428,7 +428,7 @@
               <bool>false</bool>
              </property>
              <property name="channel" stdset="0">
-              <string>oph://${IC}.preamp.offset_unit</string>
+              <string>sig://${IC}.preamp.offset_unit</string>
              </property>
             </widget>
            </item>
@@ -473,7 +473,7 @@
            <bool>false</bool>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://${IC}.preamp.set_all</string>
+           <string>sig://${IC}.preamp.set_all</string>
           </property>
           <property name="passwordProtected" stdset="0">
            <bool>false</bool>
@@ -571,7 +571,7 @@
              <string>The calculate voltage based on scaler counts and clock speed.</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.volts</string>
+             <string>sig://${IC}.volts</string>
             </property>
             <property name="enableRichText" stdset="0">
              <bool>false</bool>
@@ -640,7 +640,7 @@
              <string>The calculated current based on the voltage and pre-amp settings.</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.amps</string>
+             <string>sig://${IC}.amps</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLabel::Exponential</enum>
@@ -710,7 +710,7 @@
              <bool>false</bool>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.exposure_time</string>
+             <string>sig://${IC}.exposure_time</string>
             </property>
            </widget>
           </item>
@@ -771,7 +771,7 @@
              <string>#e##</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.frequency</string>
+             <string>sig://${IC}.frequency</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLabel::Exponential</enum>
@@ -818,7 +818,7 @@
            <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The number of counts measured by the scaler.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;This is the counts produced by the voltage-to-frequency convertor, and &lt;span style=&quot; font-weight:600;&quot;&gt;not&lt;/span&gt; the photon count.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://${IC}.counts</string>
+           <string>sig://${IC}.counts</string>
           </property>
          </widget>
         </item>
@@ -833,7 +833,7 @@
              <string>Count</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.count</string>
+             <string>sig://${IC}.count</string>
             </property>
             <property name="pressValue" stdset="0">
              <string>1</string>
@@ -849,7 +849,7 @@
              <string>Autocount</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.auto_count</string>
+             <string>sig://${IC}.auto_count</string>
             </property>
            </widget>
           </item>
@@ -876,7 +876,7 @@
              <string/>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.gate</string>
+             <string>sig://${IC}.gate</string>
             </property>
            </widget>
           </item>
@@ -886,7 +886,7 @@
              <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Stop collecting data when the tick counts for this scaler channel reaches a certain preset value.&lt;/p&gt;&lt;p&gt;To help decide this value, consider that the clock gives the number of counts that a 10V signal produces in 1 sec. So if the clock is 1e7 Hz, then a &lt;span style=&quot; font-style:italic;&quot;&gt;preset count&lt;/span&gt; of 2.5e6 would be the equivalent of counting a 2.5V signal for 1 sec.&lt;/p&gt;&lt;p&gt;The scaler will stop when either &lt;span style=&quot; font-style:italic;&quot;&gt;acquire time&lt;/span&gt; is reached, or &lt;span style=&quot; font-style:italic;&quot;&gt;preset count&lt;/span&gt; is reached for any channel on that scaler.&lt;/p&gt;&lt;p&gt;A &lt;span style=&quot; font-style:italic;&quot;&gt;preset count&lt;/span&gt; of 0 will disable this behavior.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
             </property>
             <property name="rules" stdset="0">
-             <string>[{&quot;name&quot;: &quot;Disable Present Counts&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0]&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${IC}.gate&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;The preset-counts should only be editable when the channel is set to respond to the gate. If users really want to set it but disable the gate, then they'll need to just check the box first.&quot;}]</string>
+             <string>[{&quot;name&quot;: &quot;Disable Present Counts&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0]&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${IC}.gate&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;The preset-counts should only be editable when the channel is set to respond to the gate. If users really want to set it but disable the gate, then they'll need to just check the box first.&quot;}]</string>
             </property>
             <property name="precision" stdset="0">
              <number>0</number>
@@ -910,7 +910,7 @@
              <bool>false</bool>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.preset_count</string>
+             <string>sig://${IC}.preset_count</string>
             </property>
            </widget>
           </item>
@@ -1007,7 +1007,7 @@
              <string>The measured voltage output from the pre-amp.</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.voltmeter.volts</string>
+             <string>sig://${IC}.voltmeter.volts</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLabel::Decimal</enum>
@@ -1078,7 +1078,7 @@
              <string>The calculated current based on the voltage and pre-amp settings.</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://${IC}.voltmeter.amps</string>
+             <string>sig://${IC}.voltmeter.amps</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLabel::Exponential</enum>
@@ -1121,7 +1121,7 @@
            <string>How often to report new signals for this voltmeter.</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://${IC}.voltmeter.scanning_rate</string>
+           <string>sig://${IC}.voltmeter.scanning_rate</string>
           </property>
          </widget>
         </item>
diff --git a/src/firefly/launcher.py b/src/firefly/launcher.py
index c284d535..dd8b36fd 100644
--- a/src/firefly/launcher.py
+++ b/src/firefly/launcher.py
@@ -34,12 +34,6 @@ def main(default_fullscreen=False, default_display="status"):
     from qtpy.QtGui import QPixmap
     from qtpy import QtCore
 
-    # Add plugins for handling ophyd objects
-    from pydm.data_plugins import add_plugin
-    from .ophyd_plugin import OphydPlugin
-
-    add_plugin(OphydPlugin)
-
     # Set up splash screen
     fake_app = FireflyApplication(sys.argv)
     im_dir = Path(__file__).parent.resolve()
diff --git a/src/firefly/motor.ui b/src/firefly/motor.ui
index 1a3fa488..44d49957 100644
--- a/src/firefly/motor.ui
+++ b/src/firefly/motor.ui
@@ -35,7 +35,7 @@
         <set>Qt::AlignCenter</set>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${MOTOR}_description</string>
+        <string>sig://${MOTOR}_description</string>
        </property>
       </widget>
      </item>
@@ -59,7 +59,7 @@
         <bool>true</bool>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${MOTOR}.user_readback</string>
+        <string>sig://${MOTOR}.user_readback</string>
        </property>
       </widget>
      </item>
@@ -92,7 +92,7 @@
             <string/>
            </property>
            <property name="rules" stdset="0">
-            <string>[{&quot;name&quot;: &quot;limit_autohide&quot;, &quot;property&quot;: &quot;Visible&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${MOTOR}_low_limit_switch&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
+            <string>[{&quot;name&quot;: &quot;limit_autohide&quot;, &quot;property&quot;: &quot;Visible&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${MOTOR}_low_limit_switch&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
            </property>
            <property name="alarmSensitiveContent" stdset="0">
             <bool>false</bool>
@@ -101,7 +101,7 @@
             <bool>true</bool>
            </property>
            <property name="channel" stdset="0">
-            <string>oph://${MOTOR}_low_limit_switch</string>
+            <string>sig://${MOTOR}_low_limit_switch</string>
            </property>
            <property name="onColor" stdset="0">
             <color>
@@ -145,10 +145,10 @@
             <string/>
            </property>
            <property name="rules" stdset="0">
-            <string>[{&quot;name&quot;: &quot;limit_autohide&quot;, &quot;property&quot;: &quot;Visible&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${MOTOR}_soft_limit_violation&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
+            <string>[{&quot;name&quot;: &quot;limit_autohide&quot;, &quot;property&quot;: &quot;Visible&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${MOTOR}_soft_limit_violation&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
            </property>
            <property name="channel" stdset="0">
-            <string>oph://${MOTOR}_soft_limit_violation</string>
+            <string>sig://${MOTOR}_soft_limit_violation</string>
            </property>
            <property name="onColor" stdset="0">
             <color>
@@ -180,7 +180,7 @@
           <string>5000.0281</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${MOTOR}.user_setpoint</string>
+          <string>sig://${MOTOR}.user_setpoint</string>
          </property>
         </widget>
        </item>
@@ -195,7 +195,7 @@
             <string/>
            </property>
            <property name="rules" stdset="0">
-            <string>[{&quot;name&quot;: &quot;limit_autohide&quot;, &quot;property&quot;: &quot;Visible&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${MOTOR}_high_limit_switch&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
+            <string>[{&quot;name&quot;: &quot;limit_autohide&quot;, &quot;property&quot;: &quot;Visible&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${MOTOR}_high_limit_switch&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
            </property>
            <property name="alarmSensitiveContent" stdset="0">
             <bool>false</bool>
@@ -204,7 +204,7 @@
             <bool>true</bool>
            </property>
            <property name="channel" stdset="0">
-            <string>oph://${MOTOR}_high_limit_switch</string>
+            <string>sig://${MOTOR}_high_limit_switch</string>
            </property>
            <property name="onColor" stdset="0">
             <color>
@@ -248,10 +248,10 @@
             <string/>
            </property>
            <property name="rules" stdset="0">
-            <string>[{&quot;name&quot;: &quot;limit_autohide&quot;, &quot;property&quot;: &quot;Visible&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${MOTOR}_soft_limit_violation&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
+            <string>[{&quot;name&quot;: &quot;limit_autohide&quot;, &quot;property&quot;: &quot;Visible&quot;, &quot;initial_value&quot;: &quot;False&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${MOTOR}_soft_limit_violation&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}]}]</string>
            </property>
            <property name="channel" stdset="0">
-            <string>oph://${MOTOR}_soft_limit_violation</string>
+            <string>sig://${MOTOR}_soft_limit_violation</string>
            </property>
            <property name="onColor" stdset="0">
             <color>
@@ -298,7 +298,7 @@
         <string>Stop</string>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${MOTOR}.motor_stop</string>
+        <string>sig://${MOTOR}.motor_stop</string>
        </property>
        <property name="pressValue" stdset="0">
         <string>1</string>
@@ -345,7 +345,7 @@
           <string>❰</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${MOTOR}_tweak_reverse</string>
+          <string>sig://${MOTOR}_tweak_reverse</string>
          </property>
          <property name="pressValue" stdset="0">
           <string>1</string>
@@ -379,7 +379,7 @@
           <string>Jog step</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${MOTOR}.tweak_value</string>
+          <string>sig://${MOTOR}.tweak_value</string>
          </property>
         </widget>
        </item>
@@ -415,7 +415,7 @@
           <string>❱</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${MOTOR}_tweak_forward</string>
+          <string>sig://${MOTOR}_tweak_forward</string>
          </property>
          <property name="pressValue" stdset="0">
           <string>1</string>
diff --git a/src/firefly/ophyd_plugin.py b/src/firefly/ophyd_plugin.py
deleted file mode 100644
index f416c85d..00000000
--- a/src/firefly/ophyd_plugin.py
+++ /dev/null
@@ -1,175 +0,0 @@
-import logging
-import warnings
-
-import numpy as np
-from qtpy.QtCore import Qt, Slot, QTimer
-from qtpy.QtWidgets import QApplication
-from ophyd import OphydObject
-from haven import registry, exceptions
-from pydm.data_plugins import add_plugin
-from pydm.data_plugins.plugin import PyDMConnection, PyDMPlugin
-
-
-log = logging.getLogger(__name__)
-
-
-class Connection(PyDMConnection):
-    """A pydm connection for hardware abstraction through Ophyd objects.
-
-    This makes use of Haven's instrument registry, and the channel
-    address should be the name of the device to be manipulated as
-    known to the registry. For example, if you have an epics motor
-    device named "stage_vert", then to manipulate the setpoint, use
-    "oph://stage_vert.user_setpoint", and for retrieving the readback
-    value, use "oph://stage_vert.user_readback". The exact name will
-    depend on the specifics of the ophyd device being used.
-
-    """
-
-    _cpt: OphydObject = None
-    _ctrl_vars: dict = {}
-    _known_signals: list
-    _cids: dict
-
-    def __init__(self, channel, address, protocol=None, parent=None):
-        name = address
-        self._cids = {}
-        self._known_signals = []
-        super().__init__(channel, address, protocol, parent)
-        # Resolve the device based on the ohpyd name
-        try:
-            self._cpt = registry.find(address)
-        except (AttributeError, exceptions.ComponentNotFound):
-            log.warning(f"Couldn't find ophyd plugin device: {address}")
-        else:
-            log.debug(f"Found device: {address}: {self._cpt.name}")
-        # Listen for changes
-        self.prepare_subscriptions()
-        self.add_listener(channel)  # Think this might not be needed, but should check
-
-    def prepare_subscriptions(self):
-        """Set up routines to respond to changes in the ophyd object."""
-        if self._cpt is not None:
-            log.debug(f"Preparing subscriptions for {self._cpt.name}")
-            self._cids["meta"] = self._cpt.subscribe(
-                self.update_ctrl_vars, event_type="meta", run=False
-            )
-            event_type = self._cpt._default_sub
-            self._cids[event_type] = self._cpt.subscribe(
-                self.send_new_value, event_type=event_type, run=False
-            )
-
-    def send_new_value(self, *args, **kwargs):
-        if "value" in kwargs.keys():
-            value = kwargs["value"]
-            log.debug(f"Received new value for {self._cpt.name}: {value}")
-        else:
-            log.debug(
-                f"Did not receive a new value. Skipping update for {self._cpt.name}."
-            )
-            return
-        log.debug(f"Sending new {type(value)} value for {self._cpt.name}: {value}")
-        self.new_value_signal[type(value)].emit(value)
-
-    def update_ctrl_vars(self, *args, **kwargs):
-        # Emit signal if variable has changed
-        var_signals = {
-            "connected": self.connection_state_signal,
-            "severity": self.new_severity_signal,
-            "write_access": self.write_access_signal,
-            "enum_strs": self.enum_strings_signal,
-            "units": self.unit_signal,
-            "precision": self.prec_signal,
-            "lower_ctrl_limit": self.lower_ctrl_limit_signal,
-            "upper_ctrl_limit": self.upper_ctrl_limit_signal,
-        }
-        if hasattr(self, "timestamp_signal"):
-            # The timestamp_signal is a recent addition to PyDM
-            var_signals["timestamp"] = self.timestamp_signal
-        # Process the individual control variable arguments
-        for key, signal in var_signals.items():
-            if kwargs.get(key) is not None:
-                # Use the argument value
-                val = kwargs[key]
-            else:
-                log.debug(
-                    f"Could not find {key} control variable for {self._cpt.name}."
-                )
-                continue
-            # Emit the new value if is different from last time
-            if val != self._ctrl_vars.get(key, None):
-                log.debug(f"Emitting new {key}: {val}")
-                signal.emit(val)
-                self._ctrl_vars[key] = val
-
-    def add_listener(self, channel):
-        super(Connection, self).add_listener(channel)
-        # Clear cached control variables so they can get remitted
-        self._ctrl_vars = {}
-        # If the channel is used for writing to components, hook it up
-        sig = channel.value_signal
-        is_new_signal = sig not in self._known_signals
-        if sig is not None and is_new_signal:
-            # Connect new signals
-            for dtype in [float, int, str, np.ndarray]:
-                try:
-                    sig[dtype].connect(self.set_value, Qt.QueuedConnection)
-                except KeyError:
-                    pass
-            self._known_signals.append(sig)
-        # Run the callbacks to make sure the new listener gets notified
-        self.run_callbacks()
-
-    def run_callbacks(self):
-        """Run the existing callbacks of the Ophyd object."""
-        if self._cpt is None:
-            self.connection_state_signal.emit(False)
-        else:
-            cpt = self._cpt
-            cpt.get(use_monitor=False)
-            for event_type in [cpt._default_sub, "meta"]:
-                cached = cpt._args_cache[event_type]
-                log.debug(f"Running {event_type} callbacks: {cached}")
-                if cached is not None:
-                    args, kwargs = cached
-                elif event_type == "meta":
-                    args = ()
-                    kwargs = cpt.metadata
-                else:
-                    continue
-                cid = self._cids[event_type]
-                callback = cpt._callbacks[event_type][cid]
-                callback(*args, **kwargs)
-
-    def close(self):
-        """Remove any callbacks previously set up for this connection."""
-        if self._cpt is not None:
-            for cid in self._cids.values():
-                self._cpt.unsubscribe(cid)
-
-    @Slot(float)
-    @Slot(int)
-    @Slot(str)
-    @Slot(np.ndarray)
-    def set_value(self, new_value):
-        if self._cpt is None:
-            log.warning(
-                "Cannot set value {new_value} for missing component {self._cpt.name}"
-            )
-        else:
-            log.debug(f"Setting new value for {self._cpt.name}: {new_value}")
-            try:
-                return self._cpt.set(new_value, timeout=1)
-            except RuntimeError:
-                msg = (
-                    f"Previous set for {self._cpt.name} still in progress, "
-                    f"skipping set to {new_value}"
-                )
-                warnings.warn(msg)
-                log.warning(msg)
-                return None
-
-
-class OphydPlugin(PyDMPlugin):
-    protocol = "oph"
-    connection_class = Connection
diff --git a/src/firefly/status.ui b/src/firefly/status.ui
index 21c4985c..de944018 100644
--- a/src/firefly/status.ui
+++ b/src/firefly/status.ui
@@ -135,7 +135,7 @@
            <bool>false</bool>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://monochromator_energy_user_readback</string>
+           <string>sig://monochromator_energy_user_readback</string>
           </property>
          </widget>
         </item>
@@ -196,7 +196,7 @@
            <string>Fixed offset</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://monochromator_mode</string>
+           <string>sig://monochromator_mode</string>
           </property>
          </widget>
         </item>
@@ -237,7 +237,7 @@
              <bool>true</bool>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://front_end_shutter_pss_state</string>
+             <string>sig://front_end_shutter_pss_state</string>
             </property>
             <property name="showLabels" stdset="0">
              <bool>true</bool>
@@ -285,7 +285,7 @@
              <bool>false</bool>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://front_end_shutter_open_signal</string>
+             <string>sig://front_end_shutter_open_signal</string>
             </property>
             <property name="pressValue" stdset="0">
              <string>1</string>
@@ -330,7 +330,7 @@
              <bool>true</bool>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://hutch_shutter_pss_state</string>
+             <string>sig://hutch_shutter_pss_state</string>
             </property>
             <property name="showLabels" stdset="0">
              <bool>true</bool>
@@ -366,7 +366,7 @@
              <string>Open</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://hutch_shutter_open_signal</string>
+             <string>sig://hutch_shutter_open_signal</string>
             </property>
             <property name="pressValue" stdset="0">
              <string>1</string>
@@ -385,7 +385,7 @@
              <string>Close</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://hutch_shutter_close_signal</string>
+             <string>sig://hutch_shutter_close_signal</string>
             </property>
             <property name="pressValue" stdset="0">
              <string>1</string>
@@ -447,7 +447,7 @@
              <string>746328</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://bss_proposal_proposal_id</string>
+             <string>sig://bss_proposal_proposal_id</string>
             </property>
            </widget>
           </item>
@@ -469,7 +469,7 @@
              <bool>true</bool>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://bss_proposal_title</string>
+             <string>sig://bss_proposal_title</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLabel::String</enum>
@@ -499,7 +499,7 @@
              <string>5564987</string>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://bss_esaf_esaf_id</string>
+             <string>sig://bss_esaf_esaf_id</string>
             </property>
            </widget>
           </item>
@@ -521,7 +521,7 @@
              <bool>true</bool>
             </property>
             <property name="channel" stdset="0">
-             <string>oph://bss_esaf_title</string>
+             <string>sig://bss_esaf_title</string>
             </property>
             <property name="displayFormat" stdset="0">
              <enum>PyDMLabel::String</enum>
@@ -546,7 +546,7 @@
            <string>Franklin, Watson, Crick</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_esaf_user_last_names</string>
+           <string>sig://bss_esaf_user_last_names</string>
           </property>
           <property name="displayFormat" stdset="0">
            <enum>PyDMLabel::String</enum>
@@ -569,7 +569,7 @@
            <string>1901-01-01 8:00:00</string>
           </property>
           <property name="channel" stdset="0">
-           <string>oph://bss_esaf_end_date</string>
+           <string>sig://bss_esaf_end_date</string>
           </property>
          </widget>
         </item>
diff --git a/src/firefly/tests/test_ophyd_connection.py b/src/firefly/tests/test_ophyd_connection.py
index 89234e5c..9e71ecf1 100644
--- a/src/firefly/tests/test_ophyd_connection.py
+++ b/src/firefly/tests/test_ophyd_connection.py
@@ -1,5 +1,7 @@
 import time
 import pytest
+from unittest.mock import MagicMock
+
 from ophyd import EpicsSignal, EpicsMotor, sim
 from ophyd.sim import instantiate_fake_device, make_fake_device
 from pydm.data_plugins import plugin_for_address, add_plugin
@@ -7,10 +9,9 @@
 from pydm.widgets import PyDMLineEdit
 from pydm import PyDMChannel
 from qtpy import QtCore
-from unittest.mock import MagicMock
+from typhos.plugins.core import SignalPlugin
 
 from haven import HavenMotor
-from firefly.ophyd_plugin import Connection as OphydConnection, OphydPlugin
 
 
 class DummyObject(QtCore.QObject):
@@ -19,16 +20,18 @@ class DummyObject(QtCore.QObject):
 
 @pytest.fixture()
 def sim_motor(sim_registry):
+    sim_registry.use_typhos = True
     FakeMotor = make_fake_device(HavenMotor)
     motor = FakeMotor("255idVME:m1", name="motor")
     motor.user_setpoint.sim_set_limits((0, 1000))
     sim_registry.register(motor)
+    
     return motor
 
 
 @pytest.fixture()
 def ophyd_channel(sim_motor):
-    channel = PyDMChannel(address="oph://motor.user_setpoint")
+    channel = PyDMChannel(address="sig://motor.user_setpoint")
     return channel
 
 
@@ -42,100 +45,101 @@ def ophyd_connection(sim_motor, ophyd_channel, pydm_ophyd_plugin):
 
 
 def test_ophyd_pydm_ophyd_plugin(pydm_ophyd_plugin):
-    plugin = plugin_for_address("oph://sim_detector")
-    assert isinstance(plugin, OphydPlugin)
+    plugin = plugin_for_address("sig://sim_detector")
+    assert isinstance(plugin, SignalPlugin)
 
 
+@pytest.mark.skip(reason="Moved to typhos, test should be removed in the future")
 def test_new_value(sim_motor, ophyd_connection, qtbot):
-    with qtbot.waitSignal(ophyd_connection.new_value_signal) as blocker:
+    with qtbot.waitSignal(ophyd_connection.new_value_signal, timeout=1000) as blocker:
         sim_motor.set(45.0)
     assert blocker.args[0] == 45.0
 
 
-def test_set_value(sim_motor, ophyd_connection, ophyd_channel):
-    ophyd_connection.set_value(87.0).wait()
-    assert sim_motor.user_setpoint.get(use_monitor=False) == 87.0
+# def test_set_value(sim_motor, ophyd_connection, ophyd_channel):
+#     ophyd_connection.set_value(87.0).wait()
+#     assert sim_motor.user_setpoint.get(use_monitor=False) == 87.0
 
 
 def test_missing_device(sim_motor, pydm_ophyd_plugin, qtbot, ffapp):
     """See if the connection responds properly if the device is not there."""
     connection_slot = MagicMock()
     channel = PyDMChannel(
-        address="oph://motor.nonsense_parts", connection_slot=connection_slot
+        address="sig://motor.nonsense_parts", connection_slot=connection_slot
     )
     pydm_ophyd_plugin.add_connection(channel)
 
 
-def test_update_ctrl_vals(sim_motor, ophyd_connection, qtbot):
-    conn = ophyd_connection
-    conn._ctrl_vars = {}
-    # Check if the connection state is updated
-    with qtbot.waitSignal(conn.connection_state_signal) as blocker:
-        conn.update_ctrl_vars(connected=True)
-    assert blocker.args[0] is True
-    # Make sure it isn't emitted a second time if it doesn't change
-    conn._ctrl_vars = {"connected": True}
-    conn.connection_state_signal = MagicMock()
-    conn.update_ctrl_vars(connected=True)
-    assert not conn.connection_state_signal.emit.called
-    # Check other metadata signals
-    conn._ctrl_vars = {}
-    with qtbot.waitSignal(conn.new_severity_signal) as blocker:
-        conn.update_ctrl_vars(severity=2)
-    assert blocker.args[0] == 2
-
-    conn._ctrl_vars = {}
-    with qtbot.waitSignal(conn.write_access_signal) as blocker:
-        conn.update_ctrl_vars(write_access=True)
-    assert blocker.args[0] is True
-
-    conn._ctrl_vars = {}
-    with qtbot.waitSignal(conn.enum_strings_signal) as blocker:
-        conn.update_ctrl_vars(enum_strs=("Option 1", "Option 2"))
-    assert blocker.args[0] == ("Option 1", "Option 2")
-
-    conn._ctrl_vars = {}
-    with qtbot.waitSignal(conn.unit_signal) as blocker:
-        conn.update_ctrl_vars(units="µm")
-    assert blocker.args[0] == "µm"
-
-    conn._ctrl_vars = {}
-    with qtbot.waitSignal(conn.prec_signal) as blocker:
-        conn.update_ctrl_vars(precision=5)
-    assert blocker.args[0] == 5
-
-    conn._ctrl_vars = {}
-    with qtbot.waitSignal(conn.lower_ctrl_limit_signal) as blocker:
-        conn.update_ctrl_vars(lower_ctrl_limit=-200)
-    assert blocker.args[0] == -200
-
-    conn._ctrl_vars = {}
-    with qtbot.waitSignal(conn.upper_ctrl_limit_signal) as blocker:
-        conn.update_ctrl_vars(upper_ctrl_limit=850.3)
-    assert blocker.args[0] == 850.3
-
-    conn._ctrl_vars = {}
-    with qtbot.waitSignal(conn.timestamp_signal) as blocker:
-        conn.update_ctrl_vars(timestamp=1693014035.3913143)
-    assert blocker.args[0] == 1693014035.3913143
-    {
-        "connected": True,
-        "read_access": True,
-        "write_access": True,
-        "timestamp": 1693014035.3913143,
-        "status": None,
-        "severity": None,
-        "precision": None,
-        "lower_ctrl_limit": None,
-        "upper_ctrl_limit": None,
-        "units": None,
-        "enum_strs": None,
-        "setpoint_status": None,
-        "setpoint_severity": None,
-        "setpoint_precision": None,
-        "setpoint_timestamp": None,
-        "sub_type": "meta",
-    }
+# def test_update_ctrl_vals(sim_motor, ophyd_connection, qtbot):
+#     conn = ophyd_connection
+#     conn._ctrl_vars = {}
+#     # Check if the connection state is updated
+#     with qtbot.waitSignal(conn.connection_state_signal) as blocker:
+#         conn.update_ctrl_vars(connected=True)
+#     assert blocker.args[0] is True
+#     # Make sure it isn't emitted a second time if it doesn't change
+#     conn._ctrl_vars = {"connected": True}
+#     conn.connection_state_signal = MagicMock()
+#     conn.update_ctrl_vars(connected=True)
+#     assert not conn.connection_state_signal.emit.called
+#     # Check other metadata signals
+#     conn._ctrl_vars = {}
+#     with qtbot.waitSignal(conn.new_severity_signal) as blocker:
+#         conn.update_ctrl_vars(severity=2)
+#     assert blocker.args[0] == 2
+
+#     conn._ctrl_vars = {}
+#     with qtbot.waitSignal(conn.write_access_signal) as blocker:
+#         conn.update_ctrl_vars(write_access=True)
+#     assert blocker.args[0] is True
+
+#     conn._ctrl_vars = {}
+#     with qtbot.waitSignal(conn.enum_strings_signal) as blocker:
+#         conn.update_ctrl_vars(enum_strs=("Option 1", "Option 2"))
+#     assert blocker.args[0] == ("Option 1", "Option 2")
+
+#     conn._ctrl_vars = {}
+#     with qtbot.waitSignal(conn.unit_signal) as blocker:
+#         conn.update_ctrl_vars(units="µm")
+#     assert blocker.args[0] == "µm"
+
+#     conn._ctrl_vars = {}
+#     with qtbot.waitSignal(conn.prec_signal) as blocker:
+#         conn.update_ctrl_vars(precision=5)
+#     assert blocker.args[0] == 5
+
+#     conn._ctrl_vars = {}
+#     with qtbot.waitSignal(conn.lower_ctrl_limit_signal) as blocker:
+#         conn.update_ctrl_vars(lower_ctrl_limit=-200)
+#     assert blocker.args[0] == -200
+
+#     conn._ctrl_vars = {}
+#     with qtbot.waitSignal(conn.upper_ctrl_limit_signal) as blocker:
+#         conn.update_ctrl_vars(upper_ctrl_limit=850.3)
+#     assert blocker.args[0] == 850.3
+
+#     conn._ctrl_vars = {}
+#     with qtbot.waitSignal(conn.timestamp_signal) as blocker:
+#         conn.update_ctrl_vars(timestamp=1693014035.3913143)
+#     assert blocker.args[0] == 1693014035.3913143
+#     {
+#         "connected": True,
+#         "read_access": True,
+#         "write_access": True,
+#         "timestamp": 1693014035.3913143,
+#         "status": None,
+#         "severity": None,
+#         "precision": None,
+#         "lower_ctrl_limit": None,
+#         "upper_ctrl_limit": None,
+#         "units": None,
+#         "enum_strs": None,
+#         "setpoint_status": None,
+#         "setpoint_severity": None,
+#         "setpoint_precision": None,
+#         "setpoint_timestamp": None,
+#         "sub_type": "meta",
+#     }
 
 
 def test_widget_signals(sim_motor, ffapp, qtbot):
@@ -143,8 +147,7 @@ def test_widget_signals(sim_motor, ffapp, qtbot):
     sim_motor.user_setpoint.set(5.15)
     sim_motor.user_setpoint._metadata["precision"] = 3
     window = PyDMMainWindow()
-    widget = PyDMLineEdit(parent=window, init_channel="oph://motor.user_setpoint")
-    # widget.precisionChanged(3)
+    widget = PyDMLineEdit(parent=window, init_channel="sig://motor.user_setpoint")
     ffapp.processEvents()
     time.sleep(0.05)
     assert widget.text() == "5.150"
diff --git a/src/firefly/tests/test_xrf_detector_display.py b/src/firefly/tests/test_xrf_detector_display.py
index 2f41f2b6..1e6c7139 100644
--- a/src/firefly/tests/test_xrf_detector_display.py
+++ b/src/firefly/tests/test_xrf_detector_display.py
@@ -225,7 +225,7 @@ def test_mca_region_channels(xrf_display):
     mca_display = xrf_display.mca_displays[1]
     mca_display._embedded_widget = mca_display.open_file(force=True)
     xrf_display.mca_selected(is_selected=True, mca_num=2)
-    correct_address = "oph://vortex_me4.mcas.mca2.rois.roi0.hi_chan"
+    correct_address = "sig://vortex_me4.mcas.mca2.rois.roi0.hi_chan"
     region = plot_widget.region(mca_num=2, roi_num=0)
     assert region.hi_channel.address == correct_address
     region.hi_channel.value_slot(108)
diff --git a/src/firefly/voltmeter.ui b/src/firefly/voltmeter.ui
index 7f4b3dc3..bbf16260 100644
--- a/src/firefly/voltmeter.ui
+++ b/src/firefly/voltmeter.ui
@@ -63,7 +63,7 @@
       <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
      </property>
      <property name="channel" stdset="0">
-      <string>oph://${IC}.description</string>
+      <string>sig://${IC}.description</string>
      </property>
      <property name="displayFormat" stdset="0">
       <enum>PyDMLabel::String</enum>
@@ -93,7 +93,7 @@
       <string>The measured voltage output from the pre-amp.</string>
      </property>
      <property name="channel" stdset="0">
-      <string>oph://${IC}.voltmeter.volts</string>
+      <string>sig://${IC}.voltmeter.volts</string>
      </property>
      <property name="showValue" stdset="0">
       <bool>false</bool>
@@ -212,7 +212,7 @@
           <string>The measured voltage output from the pre-amp.</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${IC}.voltmeter.volts</string>
+          <string>sig://${IC}.voltmeter.volts</string>
          </property>
          <property name="enableRichText" stdset="0">
           <bool>false</bool>
@@ -290,7 +290,7 @@
           <string>The calculated current based on the voltage from the voltmeter and the pre-amp settings.</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${IC}.voltmeter.amps</string>
+          <string>sig://${IC}.voltmeter.amps</string>
          </property>
          <property name="displayFormat" stdset="0">
           <enum>PyDMLabel::Exponential</enum>
@@ -427,13 +427,13 @@
           </size>
          </property>
          <property name="rules" stdset="0">
-          <string>[{&quot;name&quot;: &quot;Gain button lower range&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0] &lt; 27&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${IC}.preamp.sensitivity_level&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;If the sensitivity is at its highest setting, disable the button so users don't click it unnecessarily.&quot;}]</string>
+          <string>[{&quot;name&quot;: &quot;Gain button lower range&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0] &lt; 27&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${IC}.preamp.sensitivity_level&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;If the sensitivity is at its highest setting, disable the button so users don't click it unnecessarily.&quot;}]</string>
          </property>
          <property name="PyDMToolTip" stdset="0">
           <string>Decrease the pre-amp gain (higher sensitivity values)</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${IC}.preamp.sensitivity_level</string>
+          <string>sig://${IC}.preamp.sensitivity_level</string>
          </property>
          <property name="confirmMessage" stdset="0">
           <string>Are you sure you want to proceed?</string>
@@ -473,7 +473,7 @@
           </size>
          </property>
          <property name="rules" stdset="0">
-          <string>[{&quot;name&quot;: &quot;Gain button upper range&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;oph://${IC}.preamp.sensitivity_level&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;&quot;}]</string>
+          <string>[{&quot;name&quot;: &quot;Gain button upper range&quot;, &quot;property&quot;: &quot;Enable&quot;, &quot;initial_value&quot;: &quot;True&quot;, &quot;expression&quot;: &quot;ch[0] &gt; 0&quot;, &quot;channels&quot;: [{&quot;channel&quot;: &quot;sig://${IC}.preamp.sensitivity_level&quot;, &quot;trigger&quot;: true, &quot;use_enum&quot;: false}], &quot;notes&quot;: &quot;&quot;}]</string>
          </property>
          <property name="alarmSensitiveContent" stdset="0">
           <bool>false</bool>
@@ -488,7 +488,7 @@
           <bool>false</bool>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${IC}.preamp.sensitivity_level</string>
+          <string>sig://${IC}.preamp.sensitivity_level</string>
          </property>
          <property name="passwordProtected" stdset="0">
           <bool>false</bool>
@@ -570,7 +570,7 @@
           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${IC}.preamp.sensitivity_text</string>
+          <string>sig://${IC}.preamp.sensitivity_text</string>
          </property>
          <property name="displayFormat" stdset="0">
           <enum>PyDMLabel::String</enum>
diff --git a/src/firefly/xrf_detector.py b/src/firefly/xrf_detector.py
index 14973719..ea8b8302 100644
--- a/src/firefly/xrf_detector.py
+++ b/src/firefly/xrf_detector.py
@@ -172,7 +172,7 @@ def region(self, mca_num, roi_num):
         plot_item = self.ui.plot_widget.getPlotItem()
         # Create a new region item if necessary
         if key not in self._region_items.keys():
-            address = f"oph://{self.device_name}.mcas.mca{mca_num}.rois.roi{roi_num}"
+            address = f"sig://{self.device_name}.mcas.mca{mca_num}.rois.roi{roi_num}"
             color = self.region_color(mca_num=mca_num, roi_num=roi_num)
             region = ROIRegion(
                 address=address,
@@ -361,18 +361,18 @@ def leaveEvent(self, event=None):
 #     # Set up a channel for starting detector acquisition
 #     device = self.device
 #     self.start_channel = PyDMChannel(
-#         address=f"oph://{device.name}.acquire",
+#         address=f"sig://{device.name}.acquire",
 #         value_signal=self.start_all,
 #     )
 #     self.start_channel.connect()
 #     self.start_erase_channel = PyDMChannel(
-#         address=f"oph://{device.name}.acquire",
+#         address=f"sig://{device.name}.acquire",
 #         value_signal=self.start_erase,
 #     )
 #     self.start_erase_channel.connect()
 #     # This one gets (dis)connected in response to the continuous button
 #     self.acquiring_channel = PyDMChannel(
-#         address=f"oph://{device.name}.acquiring",
+#         address=f"sig://{device.name}.acquiring",
 #         value_slot=self.trigger_next,
 #     )
 
@@ -567,7 +567,7 @@ def customize_device(self):
         # Set up data channels
         self._spectrum_channels = []
         for mca_num in range(self.device.num_elements):
-            address = f"oph://{device.name}.mcas.mca{mca_num}.spectrum"
+            address = f"sig://{device.name}.mcas.mca{mca_num}.spectrum"
             channel = pydm.PyDMChannel(
                 address=address,
                 value_slot=partial(self.handle_new_spectrum, mca_num=mca_num),
diff --git a/src/firefly/xrf_detector.ui b/src/firefly/xrf_detector.ui
index d72b33f8..59e1b62e 100644
--- a/src/firefly/xrf_detector.ui
+++ b/src/firefly/xrf_detector.ui
@@ -99,7 +99,7 @@
           <bool>true</bool>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${DEV}.dwell_time</string>
+          <string>sig://${DEV}.dwell_time</string>
          </property>
         </widget>
        </item>
@@ -114,7 +114,7 @@
         <string>Continuous</string>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${DEV}.acquire_multiple</string>
+        <string>sig://${DEV}.acquire_multiple</string>
        </property>
       </widget>
      </item>
@@ -127,7 +127,7 @@
         <string>Acquire</string>
        </property>
        <property name="channel" stdset="0">
-        <string>oph://${DEV}.acquire_single</string>
+        <string>sig://${DEV}.acquire_single</string>
        </property>
        <property name="pressValue" stdset="0">
         <string>1</string>
@@ -197,7 +197,7 @@
           <bool>true</bool>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${DEV}.acquire</string>
+          <string>sig://${DEV}.acquire</string>
          </property>
          <property name="PyDMToolTip" stdset="0">
           <string/>
@@ -231,7 +231,7 @@
           <set>Qt::TextSelectableByMouse</set>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${DEV}.cam.array_counter</string>
+          <string>sig://${DEV}.cam.array_counter</string>
          </property>
         </widget>
        </item>
@@ -304,7 +304,7 @@
           <string>N/A</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${DEV}.total_count</string>
+          <string>sig://${DEV}.total_count</string>
          </property>
         </widget>
        </item>
@@ -363,7 +363,7 @@
           <bool>false</bool>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${DEV}.dead_time_average</string>
+          <string>sig://${DEV}.dead_time_average</string>
          </property>
          <property name="displayFormat" stdset="0">
           <enum>PyDMLabel::Decimal</enum>
@@ -434,7 +434,7 @@
           <string>14.0</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${DEV}.dead_time_min</string>
+          <string>sig://${DEV}.dead_time_min</string>
          </property>
          <property name="PyDMToolTip" stdset="0">
           <string>Lowest dead time across all elements.</string>
@@ -489,7 +489,7 @@
           <string>24.0</string>
          </property>
          <property name="channel" stdset="0">
-          <string>oph://${DEV}.dead_time_max</string>
+          <string>sig://${DEV}.dead_time_max</string>
          </property>
          <property name="PyDMToolTip" stdset="0">
           <string>Highest dead time across all elements.</string>
diff --git a/src/firefly/xrf_roi.ui b/src/firefly/xrf_roi.ui
index 94987524..c71959f8 100644
--- a/src/firefly/xrf_roi.ui
+++ b/src/firefly/xrf_roi.ui
@@ -90,7 +90,7 @@
          <string/>
         </property>
         <property name="channel" stdset="0">
-         <string>oph://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.use</string>
+         <string>sig://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.use</string>
         </property>
         <property name="PyDMToolTip" stdset="0">
          <string>Enable or disable this ROI.
@@ -152,7 +152,7 @@ Data will be saved either way, but disabled channels will not appear in the list
             <string>ROI Name</string>
            </property>
            <property name="channel" stdset="0">
-            <string>oph://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.label</string>
+            <string>sig://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.label</string>
            </property>
            <property name="displayFormat" stdset="0">
             <enum>PyDMLineEdit::String</enum>
@@ -176,7 +176,7 @@ Data will be saved either way, but disabled channels will not appear in the list
             <string>Lower bound</string>
            </property>
            <property name="channel" stdset="0">
-            <string>oph://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.lo_chan</string>
+            <string>sig://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.lo_chan</string>
            </property>
            <property name="displayFormat" stdset="0">
             <enum>PyDMLineEdit::Decimal</enum>
@@ -203,7 +203,7 @@ Data will be saved either way, but disabled channels will not appear in the list
             <string>Upper bound</string>
            </property>
            <property name="channel" stdset="0">
-            <string>oph://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.hi_chan</string>
+            <string>sig://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.hi_chan</string>
            </property>
            <property name="displayFormat" stdset="0">
             <enum>PyDMLineEdit::Decimal</enum>
@@ -233,7 +233,7 @@ Data will be saved either way, but disabled channels will not appear in the list
             <string>34</string>
            </property>
            <property name="channel" stdset="0">
-            <string>oph://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.size</string>
+            <string>sig://${DEV}.mcas.mca${MCA}.rois.roi${ROI}.size</string>
            </property>
            <property name="PyDMToolTip" stdset="0">
             <string>The size of the ROI, in number of channels.</string>
diff --git a/tests/conftest.py b/tests/conftest.py
index 50f11dc6..345a1d6b 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,292 +1,292 @@
-import os
-import warnings
-from pathlib import Path
-from unittest.mock import MagicMock
-from types import SimpleNamespace
-from collections import OrderedDict
-from subprocess import Popen, TimeoutExpired, PIPE
-import subprocess
-import shutil
-import time
-from unittest import mock
-import asyncio
-
-import pytest
-
-import ophyd
-from ophyd import DynamicDeviceComponent as DDC, Kind
-from ophyd.sim import (
-    instantiate_fake_device,
-    make_fake_device,
-    fake_device_cache,
-    FakeEpicsSignal,
-)
-from pydm.data_plugins import add_plugin
-
-import haven
-from haven.simulated_ioc import simulated_ioc
-from haven import load_config, registry
-from haven._iconfig import beamline_connected as _beamline_connected
-from haven.instrument.aerotech import AerotechFlyer, AerotechStage
-from haven.instrument.aps import ApsMachine
-from haven.instrument.shutter import Shutter
-from haven.instrument.camera import AravisDetector
-from haven.instrument.delay import EpicsSignalWithIO
-from haven.instrument.dxp import DxpDetector, add_mcas as add_dxp_mcas
-from haven.instrument.ion_chamber import IonChamber
-from haven.instrument.xspress import Xspress3Detector, add_mcas as add_xspress_mcas
-from firefly.application import FireflyApplication
-from firefly.ophyd_plugin import OphydPlugin
-from firefly.main_window import FireflyMainWindow
-
-
-# IOC_SCOPE = "function"
-IOC_SCOPE = "session"
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_undulator(request):
-    prefix = "ID255:"
-    pvs = dict(energy=f"{prefix}Energy.VAL")
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_undulator",
-        name="Fake undulator IOC",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["energy"],
-        request=request,
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_camera(request):
-    prefix = "255idgigeA:"
-    pvs = dict(
-        cam_acquire=f"{prefix}cam1:Acquire",
-        cam_acquire_busy=f"{prefix}cam1:AcquireBusy",
-    )
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_area_detector",
-        name="Fake IOC for a simulated machine vision camera",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["cam_acquire_busy"],
-        request=request,
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_area_detector(request):
-    prefix = "255idSimDet:"
-    pvs = dict(
-        cam_acquire=f"{prefix}cam1:Acquire",
-        cam_acquire_busy=f"{prefix}cam1:AcquireBusy",
-    )
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_area_detector",
-        name="Fake IOC for a simulated area detector",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["cam_acquire_busy"],
-        request=request,
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_bss(request):
-    prefix = "255idc:bss:"
-    pvs = dict(
-        esaf_id=f"{prefix}esaf:id",
-        esaf_cycle=f"{prefix}esaf:cycle",
-        proposal_id=f"{prefix}proposal:id",
-    )
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_apsbss",
-        name="Fake IOC for APS beamline scheduling system (BSS)",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["esaf_cycle"],
-        request=request,
-    )
-
-
-def run_fake_ioc(
-    module_name,
-    prefix: str,
-    request,
-    name="Fake IOC",
-    pvs=None,
-    pv_to_check: str = None,
-):
-    if pvs is None:
-        pvs = {}
-    pytest.importorskip("caproto.tests.conftest")
-    from caproto.tests.conftest import run_example_ioc, poll_readiness
-
-    process = run_example_ioc(
-        module_name=module_name,
-        request=request,
-        pv_to_check=None,
-        args=("--prefix", prefix, "--list-pvs", "-v"),
-        very_verbose=False,
-    )
-    # Verify the IOC started
-    if pv_to_check is not None:
-        poll_timeout, poll_attempts = 1.0, 30
-        poll_readiness(
-            pv_to_check, timeout=poll_timeout, attempts=poll_attempts, process=process
-        )
-    return SimpleNamespace(
-        process=process, prefix=prefix, name=name, pvs=pvs, type="caproto"
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_scaler(request):
-    prefix = "255idVME:scaler1"
-    pvs = dict(calc=f"{prefix}_calc2.VAL")
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_scaler",
-        name="Fake scaler IOC",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["calc"],
-        request=request,
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_ptc10(request):
-    prefix = "255idptc10:"
-    pvs = dict(
-        pid1_voltage=f"{prefix}5A:output",
-        pid1_voltage_rbv=f"{prefix}5A:output_RBV",
-        tc1_temperature=f"{prefix}2A:temperature",
-    )
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_ptc10",
-        name="Fake PTC10 temperature controller IOC",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["tc1_temperature"],
-        request=request,
-    )
-
-
-@pytest.fixture(scope="session")
-def pydm_ophyd_plugin():
-    return add_plugin(OphydPlugin)
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_motor(request):
-    prefix = "255idVME:"
-    pvs = dict(m1=f"{prefix}m1", m2=f"{prefix}m2", m3=f"{prefix}m3", m4=f"{prefix}m4")
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_motor",
-        name="Fake motor IOC",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["m1"],
-        request=request,
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_preamp(request):
-    prefix = "255idc:"
-    pvs = dict(
-        preamp1_sens_num=f"{prefix}SR01:sens_num",
-        preamp2_sens_num=f"{prefix}SR02:sens_num",
-        preamp3_sens_num=f"{prefix}SR03:sens_num",
-        preamp4_sens_num=f"{prefix}SR04:sens_num",
-        preamp1_sens_unit=f"{prefix}SR01:sens_unit",
-        preamp2_sens_unit=f"{prefix}SR02:sens_unit",
-        preamp3_sens_unit=f"{prefix}SR03:sens_unit",
-        preamp4_sens_unit=f"{prefix}SR04:sens_unit",
-        preamp1_offset_num=f"{prefix}SR01:offset_num",
-        preamp2_offset_num=f"{prefix}SR02:offset_num",
-        preamp3_offset_num=f"{prefix}SR03:offset_num",
-        preamp4_offset_num=f"{prefix}SR04:offset_num",
-        preamp1_offset_unit=f"{prefix}SR01:offset_unit",
-        preamp2_offset_unit=f"{prefix}SR02:offset_unit",
-        preamp3_offset_unit=f"{prefix}SR03:offset_unit",
-        preamp4_offset_unit=f"{prefix}SR04:offset_unit",
-    )
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_preamp",
-        name="Fake preamp IOC",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["preamp1_sens_num"],
-        request=request,
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_simple(request):
-    prefix = "simple:"
-    pvs = dict(
-        A=f"{prefix}A",
-        B=f"{prefix}B",
-        C=f"{prefix}C",
-    )
-    pv_to_check = pvs["A"]
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_simple",
-        name="Fake simple IOC",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pv_to_check,
-        request=request,
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_mono(request):
-    prefix = "255idMono:"
-    pvs = dict(
-        bragg=f"{prefix}ACS:m3",
-        energy=f"{prefix}Energy",
-        id_tracking=f"{prefix}ID_tracking",
-        id_offset=f"{prefix}ID_offset",
-    )
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_mono",
-        name="Fake mono IOC",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["energy"],
-        request=request,
-    )
-
-
-@pytest.fixture(scope=IOC_SCOPE)
-def ioc_dxp(request):
-    prefix = "255idDXP:"
-    pvs = dict(acquiring=f"{prefix}Acquiring")
-    return run_fake_ioc(
-        module_name="haven.tests.ioc_dxp",
-        name="Fake DXP-based detector IOC",
-        prefix=prefix,
-        pvs=pvs,
-        pv_to_check=pvs["acquiring"],
-        request=request,
-    )
-
-
-# Simulated devices
-@pytest.fixture()
-def queue_app(ffapp):
-    """An application that is set up to interact (fakely) with the queue
-    server.
-
-    """
-    warnings.warn("queue_app is deprecated, just use ffapp instead.")
-    return ffapp
-
-
-@pytest.fixture()
-def sim_vortex(dxp):
-    warnings.warn("sim_vortex is deprecated, just use ``dxp`` instead.")
-    return dxp
+# import os
+# import warnings
+# from pathlib import Path
+# from unittest.mock import MagicMock
+# from types import SimpleNamespace
+# from collections import OrderedDict
+# from subprocess import Popen, TimeoutExpired, PIPE
+# import subprocess
+# import shutil
+# import time
+# from unittest import mock
+# import asyncio
+
+# import pytest
+
+# import ophyd
+# from ophyd import DynamicDeviceComponent as DDC, Kind
+# from ophyd.sim import (
+#     instantiate_fake_device,
+#     make_fake_device,
+#     fake_device_cache,
+#     FakeEpicsSignal,
+# )
+# from pydm.data_plugins import add_plugin
+
+# import haven
+# from haven.simulated_ioc import simulated_ioc
+# from haven import load_config, registry
+# from haven._iconfig import beamline_connected as _beamline_connected
+# from haven.instrument.aerotech import AerotechFlyer, AerotechStage
+# from haven.instrument.aps import ApsMachine
+# from haven.instrument.shutter import Shutter
+# from haven.instrument.camera import AravisDetector
+# from haven.instrument.delay import EpicsSignalWithIO
+# from haven.instrument.dxp import DxpDetector, add_mcas as add_dxp_mcas
+# from haven.instrument.ion_chamber import IonChamber
+# from haven.instrument.xspress import Xspress3Detector, add_mcas as add_xspress_mcas
+# from firefly.application import FireflyApplication
+# from firefly.ophyd_plugin import OphydPlugin
+# from firefly.main_window import FireflyMainWindow
+
+
+# # IOC_SCOPE = "function"
+# IOC_SCOPE = "session"
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_undulator(request):
+#     prefix = "ID255:"
+#     pvs = dict(energy=f"{prefix}Energy.VAL")
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_undulator",
+#         name="Fake undulator IOC",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["energy"],
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_camera(request):
+#     prefix = "255idgigeA:"
+#     pvs = dict(
+#         cam_acquire=f"{prefix}cam1:Acquire",
+#         cam_acquire_busy=f"{prefix}cam1:AcquireBusy",
+#     )
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_area_detector",
+#         name="Fake IOC for a simulated machine vision camera",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["cam_acquire_busy"],
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_area_detector(request):
+#     prefix = "255idSimDet:"
+#     pvs = dict(
+#         cam_acquire=f"{prefix}cam1:Acquire",
+#         cam_acquire_busy=f"{prefix}cam1:AcquireBusy",
+#     )
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_area_detector",
+#         name="Fake IOC for a simulated area detector",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["cam_acquire_busy"],
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_bss(request):
+#     prefix = "255idc:bss:"
+#     pvs = dict(
+#         esaf_id=f"{prefix}esaf:id",
+#         esaf_cycle=f"{prefix}esaf:cycle",
+#         proposal_id=f"{prefix}proposal:id",
+#     )
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_apsbss",
+#         name="Fake IOC for APS beamline scheduling system (BSS)",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["esaf_cycle"],
+#         request=request,
+#     )
+
+
+# def run_fake_ioc(
+#     module_name,
+#     prefix: str,
+#     request,
+#     name="Fake IOC",
+#     pvs=None,
+#     pv_to_check: str = None,
+# ):
+#     if pvs is None:
+#         pvs = {}
+#     pytest.importorskip("caproto.tests.conftest")
+#     from caproto.tests.conftest import run_example_ioc, poll_readiness
+
+#     process = run_example_ioc(
+#         module_name=module_name,
+#         request=request,
+#         pv_to_check=None,
+#         args=("--prefix", prefix, "--list-pvs", "-v"),
+#         very_verbose=False,
+#     )
+#     # Verify the IOC started
+#     if pv_to_check is not None:
+#         poll_timeout, poll_attempts = 1.0, 30
+#         poll_readiness(
+#             pv_to_check, timeout=poll_timeout, attempts=poll_attempts, process=process
+#         )
+#     return SimpleNamespace(
+#         process=process, prefix=prefix, name=name, pvs=pvs, type="caproto"
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_scaler(request):
+#     prefix = "255idVME:scaler1"
+#     pvs = dict(calc=f"{prefix}_calc2.VAL")
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_scaler",
+#         name="Fake scaler IOC",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["calc"],
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_ptc10(request):
+#     prefix = "255idptc10:"
+#     pvs = dict(
+#         pid1_voltage=f"{prefix}5A:output",
+#         pid1_voltage_rbv=f"{prefix}5A:output_RBV",
+#         tc1_temperature=f"{prefix}2A:temperature",
+#     )
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_ptc10",
+#         name="Fake PTC10 temperature controller IOC",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["tc1_temperature"],
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope="session")
+# def pydm_ophyd_plugin():
+#     return add_plugin(OphydPlugin)
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_motor(request):
+#     prefix = "255idVME:"
+#     pvs = dict(m1=f"{prefix}m1", m2=f"{prefix}m2", m3=f"{prefix}m3", m4=f"{prefix}m4")
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_motor",
+#         name="Fake motor IOC",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["m1"],
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_preamp(request):
+#     prefix = "255idc:"
+#     pvs = dict(
+#         preamp1_sens_num=f"{prefix}SR01:sens_num",
+#         preamp2_sens_num=f"{prefix}SR02:sens_num",
+#         preamp3_sens_num=f"{prefix}SR03:sens_num",
+#         preamp4_sens_num=f"{prefix}SR04:sens_num",
+#         preamp1_sens_unit=f"{prefix}SR01:sens_unit",
+#         preamp2_sens_unit=f"{prefix}SR02:sens_unit",
+#         preamp3_sens_unit=f"{prefix}SR03:sens_unit",
+#         preamp4_sens_unit=f"{prefix}SR04:sens_unit",
+#         preamp1_offset_num=f"{prefix}SR01:offset_num",
+#         preamp2_offset_num=f"{prefix}SR02:offset_num",
+#         preamp3_offset_num=f"{prefix}SR03:offset_num",
+#         preamp4_offset_num=f"{prefix}SR04:offset_num",
+#         preamp1_offset_unit=f"{prefix}SR01:offset_unit",
+#         preamp2_offset_unit=f"{prefix}SR02:offset_unit",
+#         preamp3_offset_unit=f"{prefix}SR03:offset_unit",
+#         preamp4_offset_unit=f"{prefix}SR04:offset_unit",
+#     )
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_preamp",
+#         name="Fake preamp IOC",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["preamp1_sens_num"],
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_simple(request):
+#     prefix = "simple:"
+#     pvs = dict(
+#         A=f"{prefix}A",
+#         B=f"{prefix}B",
+#         C=f"{prefix}C",
+#     )
+#     pv_to_check = pvs["A"]
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_simple",
+#         name="Fake simple IOC",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pv_to_check,
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_mono(request):
+#     prefix = "255idMono:"
+#     pvs = dict(
+#         bragg=f"{prefix}ACS:m3",
+#         energy=f"{prefix}Energy",
+#         id_tracking=f"{prefix}ID_tracking",
+#         id_offset=f"{prefix}ID_offset",
+#     )
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_mono",
+#         name="Fake mono IOC",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["energy"],
+#         request=request,
+#     )
+
+
+# @pytest.fixture(scope=IOC_SCOPE)
+# def ioc_dxp(request):
+#     prefix = "255idDXP:"
+#     pvs = dict(acquiring=f"{prefix}Acquiring")
+#     return run_fake_ioc(
+#         module_name="haven.tests.ioc_dxp",
+#         name="Fake DXP-based detector IOC",
+#         prefix=prefix,
+#         pvs=pvs,
+#         pv_to_check=pvs["acquiring"],
+#         request=request,
+#     )
+
+
+# # Simulated devices
+# @pytest.fixture()
+# def queue_app(ffapp):
+#     """An application that is set up to interact (fakely) with the queue
+#     server.
+
+#     """
+#     warnings.warn("queue_app is deprecated, just use ffapp instead.")
+#     return ffapp
+
+
+# @pytest.fixture()
+# def sim_vortex(dxp):
+#     warnings.warn("sim_vortex is deprecated, just use ``dxp`` instead.")
+#     return dxp

From 0627544ebea7af9edba2e885bd9580ed9a76fc35 Mon Sep 17 00:00:00 2001
From: Mark Wolfman <canismarko@gmail.com>
Date: Tue, 14 Nov 2023 21:08:06 -0600
Subject: [PATCH 3/9] Stripped the tests down to the minimum need to cause a
 segfault on CI.

---
 .github/workflows/ci.yml                      |   2 +-
 src/firefly/application.py                    |   4 +-
 src/firefly/tests/test_run_browser.py         | 416 ++++++++--------
 src/firefly/tests/test_voltmeters.py          | 130 ++---
 .../tests/test_xrf_detector_display.py        | 460 +++++++++---------
 5 files changed, 508 insertions(+), 504 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4404bc4f..ce235adb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -45,4 +45,4 @@ jobs:
         # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
     - name: Test with pytest
-      run: pytest --timeout=120
+      run: pytest --timeout=120 src/firefly/tests/test_run_browser.py src/firefly/tests/test_voltmeters.py src/firefly/tests/test_xrf_detector_display.py
diff --git a/src/firefly/application.py b/src/firefly/application.py
index c61442cd..56d106bf 100644
--- a/src/firefly/application.py
+++ b/src/firefly/application.py
@@ -268,11 +268,13 @@ def _prepare_device_windows(self, device_label: str, attr_name: str):
         # if attr_name == "ion_chamber":
         #     breakpoint()
         for device in devices:
+            # Check that we don't already have actions set
+            # Create the window action
             action = QtWidgets.QAction(self)
             action.setObjectName(f"actionShow_{attr_name}_{device.name}")
             action.setText(device.name)
             actions[device.name] = action
-            # Create a slot for opening the motor window
+            # Create a slot for opening the device window
             slot = getattr(self, f"show_{attr_name}_window")
             slot = partial(slot, device=device)
             action.triggered.connect(slot)
diff --git a/src/firefly/tests/test_run_browser.py b/src/firefly/tests/test_run_browser.py
index 4e43cdb0..0917ad73 100644
--- a/src/firefly/tests/test_run_browser.py
+++ b/src/firefly/tests/test_run_browser.py
@@ -27,7 +27,7 @@ def wait_for_runs_model(display, qtbot):
     with qtbot.waitSignal(display.runs_model_changed):
         pass
 
-
+# Some mocked test data
 run1 = pd.DataFrame(
     {
         "energy_energy": np.linspace(8300, 8400, num=100),
@@ -94,10 +94,6 @@ def client():
         yield client["255id_testing"]
         
 
-def test_client_fixture(client):
-    """Does the client fixture load without stalling the test runner?"""
-    pass
-
 @pytest.fixture()
 def display(ffapp, client, qtbot):
     display = RunBrowserDisplay(root_node=client)
@@ -107,13 +103,19 @@ def display(ffapp, client, qtbot):
     finally:
         display._thread.quit()
         display._thread.wait(msecs=5000)
+        assert not display._thread.isRunning()
+
+
+# def test_client_fixture(client):
+#     """Does the client fixture load without stalling the test runner?"""
+#     pass
 
 
-def test_run_viewer_action(ffapp, monkeypatch):
-    monkeypatch.setattr(ffapp, "create_window", MagicMock())
-    assert hasattr(ffapp, "show_run_browser_action")
-    ffapp.show_run_browser_action.trigger()
-    assert isinstance(ffapp.windows["run_browser"], MagicMock)
+# def test_run_viewer_action(ffapp, monkeypatch):
+#     monkeypatch.setattr(ffapp, "create_window", MagicMock())
+#     assert hasattr(ffapp, "show_run_browser_action")
+#     ffapp.show_run_browser_action.trigger()
+#     assert isinstance(ffapp.windows["run_browser"], MagicMock)
 
 
 def test_load_runs(display):
@@ -121,200 +123,200 @@ def test_load_runs(display):
     assert display.ui.runs_total_label.text() == str(display.runs_model.rowCount())
 
 
-def test_update_selected_runs(qtbot, display):
-    # Change the proposal item
-    selection_model = display.ui.run_tableview.selectionModel()
-    item = display.runs_model.item(0, 1)
-    assert item is not None
-    rect = display.run_tableview.visualRect(item.index())
-    with qtbot.waitSignal(display._db_worker.selected_runs_changed):
-        qtbot.mouseClick(
-            display.run_tableview.viewport(), Qt.LeftButton, pos=rect.center()
-        )
-    # Check that the runs were saved
-    assert len(display._db_worker.selected_runs) > 0
-
-
-def test_metadata(qtbot, display):
-    # Change the proposal item
-    selection_model = display.ui.run_tableview.selectionModel()
-    item = display.runs_model.item(0, 1)
-    assert item is not None
-    rect = display.run_tableview.visualRect(item.index())
-    with qtbot.waitSignal(display._db_worker.selected_runs_changed):
-        qtbot.mouseClick(
-            display.run_tableview.viewport(), Qt.LeftButton, pos=rect.center()
-        )
-    # Check that the metadata was set properly in the Metadata tab
-    metadata_doc = display.ui.metadata_textedit.document()
-    text = display.ui.metadata_textedit.document().toPlainText()
-    assert "xafs_scan" in text
-
-
-def test_1d_plot_signals(client, display):
-    # Check that the 1D plot was created
-    plot_widget = display.ui.plot_1d_view
-    plot_item = display.plot_1d_item
-    assert isinstance(plot_widget, PlotWidget)
-    assert isinstance(plot_item, PlotItem)
-    # Update the list of runs and see if the controsl get updated
-    display._db_worker.selected_runs = client.values()
-    display._db_worker.selected_runs_changed.emit([])
-    # Check signals in checkboxes
-    for combobox in [
-        display.ui.multi_signal_x_combobox,
-        display.ui.signal_y_combobox,
-        display.ui.signal_r_combobox,
-        display.ui.signal_x_combobox,
-    ]:
-        assert (
-            combobox.findText("energy_energy") > -1
-        ), f"energy_energy signal not in {combobox.objectName()}."
-
-
-def test_1d_plot_signal_memory(client, display):
-    """Do we remember the signals that were previously selected."""
-    # Check that the 1D plot was created
-    plot_widget = display.ui.plot_1d_view
-    plot_item = display.plot_1d_item
-    assert isinstance(plot_widget, PlotWidget)
-    assert isinstance(plot_item, PlotItem)
-    # Update the list of runs and see if the controls get updated
-    display._db_worker.selected_runs = client.values()
-    display.update_1d_signals()
-    # Check signals in comboboxes
-    cb = display.ui.signal_y_combobox
-    assert cb.currentText() == "energy_energy"
-    cb.setCurrentIndex(1)
-    assert cb.currentText() == "energy_id_energy_readback"
-    # Update the combobox signals and make sure the text didn't change
-    display.update_1d_signals()
-    assert cb.currentText() == "energy_id_energy_readback"
-
-
-def test_1d_hinted_signals(client, display):
-    display.ui.plot_1d_hints_checkbox.setChecked(True)
-    # Check that the 1D plot was created
-    plot_widget = display.ui.plot_1d_view
-    plot_item = display.plot_1d_item
-    assert isinstance(plot_widget, PlotWidget)
-    assert isinstance(plot_item, PlotItem)
-    # Update the list of runs and see if the controsl get updated
-    display._db_worker.selected_runs = client.values()
-    display.update_1d_signals()
-    # Check signals in checkboxes
-    combobox = display.ui.signal_x_combobox
-    assert (
-        combobox.findText("energy_energy") > -1
-    ), f"hinted signal not in {combobox.objectName()}."
-    assert (
-        combobox.findText("It_net_counts") == -1
-    ), f"unhinted signal found in {combobox.objectName()}."
-
-
-@pytest.mark.skip(reason="Need to figure out why tiled fails with this test.")
-def test_update_1d_plot(client, display, qtbot):
-    run = client.values()[0]
-    run_data = run["primary"]["data"].read()
-    expected_xdata = run_data.energy_energy
-    expected_ydata = np.log(run_data.I0_net_counts / run_data.It_net_counts)
-    expected_ydata = np.gradient(expected_ydata, expected_xdata)
-    with qtbot.waitSignal(display.plot_1d_changed):
-        display._db_worker.selected_runs_changed.emit([])
-    # Set the controls to describe the data we want to test
-    x_combobox = display.ui.signal_x_combobox
-    x_combobox.addItem("energy_energy")
-    x_combobox.setCurrentText("energy_energy")
-    y_combobox = display.ui.signal_y_combobox
-    y_combobox.addItem("It_net_counts")
-    y_combobox.setCurrentText("It_net_counts")
-    r_combobox = display.ui.signal_r_combobox
-    r_combobox.addItem("I0_net_counts")
-    r_combobox.setCurrentText("I0_net_counts")
-    display.ui.signal_r_checkbox.setChecked(True)
-    display.ui.logarithm_checkbox.setChecked(True)
-    display.ui.invert_checkbox.setChecked(True)
-    display.ui.gradient_checkbox.setChecked(True)
-    # Update the plots
-    display._db_worker.selected_runs = [run]
-    display.update_1d_plot()
-    # Check that the data were added
-    data_item = display.plot_1d_item.listDataItems()[0]
-    xdata, ydata = data_item.getData()
-    np.testing.assert_almost_equal(xdata, expected_xdata)
-    np.testing.assert_almost_equal(ydata, expected_ydata)
-
-
-def test_update_multi_plot(client, display, qtbot):
-    run = client.values()[0]
-    run_data = run["primary"]["data"].read()
-    expected_xdata = run_data.energy_energy
-    expected_ydata = np.log(run_data.I0_net_counts / run_data.It_net_counts)
-    expected_ydata = np.gradient(expected_ydata, expected_xdata)
-    with qtbot.waitSignal(display.plot_1d_changed):
-        display._db_worker.selected_runs_changed.emit([])
-    # Configure signals
-    display.ui.multi_signal_x_combobox.addItem("energy_energy")
-    display.ui.multi_signal_x_combobox.setCurrentText("energy_energy")
-    display.multi_y_signals = ["energy_energy"]
-    display._db_worker.selected_runs = [run]
-    # Update the plots
-    display.update_multi_plot()
-    # Check that the data were added
-    # data_item = display._multiplot_items[0].listDataItems()[0]
-    # xdata, ydata = data_item.getData()
-    # np.testing.assert_almost_equal(xdata, expected_xdata)
-    # np.testing.assert_almost_equal(ydata, expected_ydata)
-
-
-def test_filter_controls(client, display, qtbot):
-    # Does editing text change the filters?
-    display.ui.filter_user_combobox.setCurrentText("")
-    with qtbot.waitSignal(display.filters_changed):
-        qtbot.keyClicks(display.ui.filter_user_combobox, "wolfman")
-    # Set some values for the rest of the controls
-    display.ui.filter_proposal_combobox.setCurrentText("12345")
-    display.ui.filter_esaf_combobox.setCurrentText("678901")
-    display.ui.filter_current_proposal_checkbox.setChecked(True)
-    display.ui.filter_current_esaf_checkbox.setChecked(True)
-    display.ui.filter_plan_combobox.addItem("cake")
-    display.ui.filter_plan_combobox.setCurrentText("cake")
-    display.ui.filter_full_text_lineedit.setText("Aperature Science")
-    display.ui.filter_edge_combobox.setCurrentText("U-K")
-    display.ui.filter_sample_combobox.setCurrentText("Pb.*")
-    with qtbot.waitSignal(display.filters_changed) as blocker:
-        display.update_filters()
-    # Check if the filters were update correctly
-    filters = blocker.args[0]
-    assert filters == {
-        "user": "wolfman",
-        "proposal": "12345",
-        "esaf": "678901",
-        "use_current_proposal": True,
-        "use_current_esaf": True,
-        "exit_status": "success",
-        "plan": "cake",
-        "full_text": "Aperature Science",
-        "edge": "U-K",
-        "sample": "Pb.*",
-    }
-
-
-def test_filter_runs(client, qtbot):
-    worker = DatabaseWorker(root_node=client)
-    worker._filters["plan"] = "xafs_scan"
-    with qtbot.waitSignal(worker.all_runs_changed) as blocker:
-        worker.load_all_runs()
-    # Check that the runs were filtered
-    runs = blocker.args[0]
-    assert len(runs) == 1
-
-
-def test_distinct_fields(client, qtbot, display):
-    worker = DatabaseWorker(root_node=client)
-    with qtbot.waitSignal(worker.distinct_fields_changed) as blocker:
-        worker.load_distinct_fields()
-    # Check that the dictionary has the right structure
-    distinct_fields = blocker.args[0]
-    for key in ["sample_name"]:
-        assert key in distinct_fields.keys()
+# def test_update_selected_runs(qtbot, display):
+#     # Change the proposal item
+#     selection_model = display.ui.run_tableview.selectionModel()
+#     item = display.runs_model.item(0, 1)
+#     assert item is not None
+#     rect = display.run_tableview.visualRect(item.index())
+#     with qtbot.waitSignal(display._db_worker.selected_runs_changed):
+#         qtbot.mouseClick(
+#             display.run_tableview.viewport(), Qt.LeftButton, pos=rect.center()
+#         )
+#     # Check that the runs were saved
+#     assert len(display._db_worker.selected_runs) > 0
+
+
+# def test_metadata(qtbot, display):
+#     # Change the proposal item
+#     selection_model = display.ui.run_tableview.selectionModel()
+#     item = display.runs_model.item(0, 1)
+#     assert item is not None
+#     rect = display.run_tableview.visualRect(item.index())
+#     with qtbot.waitSignal(display._db_worker.selected_runs_changed):
+#         qtbot.mouseClick(
+#             display.run_tableview.viewport(), Qt.LeftButton, pos=rect.center()
+#         )
+#     # Check that the metadata was set properly in the Metadata tab
+#     metadata_doc = display.ui.metadata_textedit.document()
+#     text = display.ui.metadata_textedit.document().toPlainText()
+#     assert "xafs_scan" in text
+
+
+# def test_1d_plot_signals(client, display):
+#     # Check that the 1D plot was created
+#     plot_widget = display.ui.plot_1d_view
+#     plot_item = display.plot_1d_item
+#     assert isinstance(plot_widget, PlotWidget)
+#     assert isinstance(plot_item, PlotItem)
+#     # Update the list of runs and see if the controsl get updated
+#     display._db_worker.selected_runs = client.values()
+#     display._db_worker.selected_runs_changed.emit([])
+#     # Check signals in checkboxes
+#     for combobox in [
+#         display.ui.multi_signal_x_combobox,
+#         display.ui.signal_y_combobox,
+#         display.ui.signal_r_combobox,
+#         display.ui.signal_x_combobox,
+#     ]:
+#         assert (
+#             combobox.findText("energy_energy") > -1
+#         ), f"energy_energy signal not in {combobox.objectName()}."
+
+
+# def test_1d_plot_signal_memory(client, display):
+#     """Do we remember the signals that were previously selected."""
+#     # Check that the 1D plot was created
+#     plot_widget = display.ui.plot_1d_view
+#     plot_item = display.plot_1d_item
+#     assert isinstance(plot_widget, PlotWidget)
+#     assert isinstance(plot_item, PlotItem)
+#     # Update the list of runs and see if the controls get updated
+#     display._db_worker.selected_runs = client.values()
+#     display.update_1d_signals()
+#     # Check signals in comboboxes
+#     cb = display.ui.signal_y_combobox
+#     assert cb.currentText() == "energy_energy"
+#     cb.setCurrentIndex(1)
+#     assert cb.currentText() == "energy_id_energy_readback"
+#     # Update the combobox signals and make sure the text didn't change
+#     display.update_1d_signals()
+#     assert cb.currentText() == "energy_id_energy_readback"
+
+
+# def test_1d_hinted_signals(client, display):
+#     display.ui.plot_1d_hints_checkbox.setChecked(True)
+#     # Check that the 1D plot was created
+#     plot_widget = display.ui.plot_1d_view
+#     plot_item = display.plot_1d_item
+#     assert isinstance(plot_widget, PlotWidget)
+#     assert isinstance(plot_item, PlotItem)
+#     # Update the list of runs and see if the controsl get updated
+#     display._db_worker.selected_runs = client.values()
+#     display.update_1d_signals()
+#     # Check signals in checkboxes
+#     combobox = display.ui.signal_x_combobox
+#     assert (
+#         combobox.findText("energy_energy") > -1
+#     ), f"hinted signal not in {combobox.objectName()}."
+#     assert (
+#         combobox.findText("It_net_counts") == -1
+#     ), f"unhinted signal found in {combobox.objectName()}."
+
+
+# @pytest.mark.skip(reason="Need to figure out why tiled fails with this test.")
+# def test_update_1d_plot(client, display, qtbot):
+#     run = client.values()[0]
+#     run_data = run["primary"]["data"].read()
+#     expected_xdata = run_data.energy_energy
+#     expected_ydata = np.log(run_data.I0_net_counts / run_data.It_net_counts)
+#     expected_ydata = np.gradient(expected_ydata, expected_xdata)
+#     with qtbot.waitSignal(display.plot_1d_changed):
+#         display._db_worker.selected_runs_changed.emit([])
+#     # Set the controls to describe the data we want to test
+#     x_combobox = display.ui.signal_x_combobox
+#     x_combobox.addItem("energy_energy")
+#     x_combobox.setCurrentText("energy_energy")
+#     y_combobox = display.ui.signal_y_combobox
+#     y_combobox.addItem("It_net_counts")
+#     y_combobox.setCurrentText("It_net_counts")
+#     r_combobox = display.ui.signal_r_combobox
+#     r_combobox.addItem("I0_net_counts")
+#     r_combobox.setCurrentText("I0_net_counts")
+#     display.ui.signal_r_checkbox.setChecked(True)
+#     display.ui.logarithm_checkbox.setChecked(True)
+#     display.ui.invert_checkbox.setChecked(True)
+#     display.ui.gradient_checkbox.setChecked(True)
+#     # Update the plots
+#     display._db_worker.selected_runs = [run]
+#     display.update_1d_plot()
+#     # Check that the data were added
+#     data_item = display.plot_1d_item.listDataItems()[0]
+#     xdata, ydata = data_item.getData()
+#     np.testing.assert_almost_equal(xdata, expected_xdata)
+#     np.testing.assert_almost_equal(ydata, expected_ydata)
+
+
+# def test_update_multi_plot(client, display, qtbot):
+#     run = client.values()[0]
+#     run_data = run["primary"]["data"].read()
+#     expected_xdata = run_data.energy_energy
+#     expected_ydata = np.log(run_data.I0_net_counts / run_data.It_net_counts)
+#     expected_ydata = np.gradient(expected_ydata, expected_xdata)
+#     with qtbot.waitSignal(display.plot_1d_changed):
+#         display._db_worker.selected_runs_changed.emit([])
+#     # Configure signals
+#     display.ui.multi_signal_x_combobox.addItem("energy_energy")
+#     display.ui.multi_signal_x_combobox.setCurrentText("energy_energy")
+#     display.multi_y_signals = ["energy_energy"]
+#     display._db_worker.selected_runs = [run]
+#     # Update the plots
+#     display.update_multi_plot()
+#     # Check that the data were added
+#     # data_item = display._multiplot_items[0].listDataItems()[0]
+#     # xdata, ydata = data_item.getData()
+#     # np.testing.assert_almost_equal(xdata, expected_xdata)
+#     # np.testing.assert_almost_equal(ydata, expected_ydata)
+
+
+# def test_filter_controls(client, display, qtbot):
+#     # Does editing text change the filters?
+#     display.ui.filter_user_combobox.setCurrentText("")
+#     with qtbot.waitSignal(display.filters_changed):
+#         qtbot.keyClicks(display.ui.filter_user_combobox, "wolfman")
+#     # Set some values for the rest of the controls
+#     display.ui.filter_proposal_combobox.setCurrentText("12345")
+#     display.ui.filter_esaf_combobox.setCurrentText("678901")
+#     display.ui.filter_current_proposal_checkbox.setChecked(True)
+#     display.ui.filter_current_esaf_checkbox.setChecked(True)
+#     display.ui.filter_plan_combobox.addItem("cake")
+#     display.ui.filter_plan_combobox.setCurrentText("cake")
+#     display.ui.filter_full_text_lineedit.setText("Aperature Science")
+#     display.ui.filter_edge_combobox.setCurrentText("U-K")
+#     display.ui.filter_sample_combobox.setCurrentText("Pb.*")
+#     with qtbot.waitSignal(display.filters_changed) as blocker:
+#         display.update_filters()
+#     # Check if the filters were update correctly
+#     filters = blocker.args[0]
+#     assert filters == {
+#         "user": "wolfman",
+#         "proposal": "12345",
+#         "esaf": "678901",
+#         "use_current_proposal": True,
+#         "use_current_esaf": True,
+#         "exit_status": "success",
+#         "plan": "cake",
+#         "full_text": "Aperature Science",
+#         "edge": "U-K",
+#         "sample": "Pb.*",
+#     }
+
+
+# def test_filter_runs(client, qtbot):
+#     worker = DatabaseWorker(root_node=client)
+#     worker._filters["plan"] = "xafs_scan"
+#     with qtbot.waitSignal(worker.all_runs_changed) as blocker:
+#         worker.load_all_runs()
+#     # Check that the runs were filtered
+#     runs = blocker.args[0]
+#     assert len(runs) == 1
+
+
+# def test_distinct_fields(client, qtbot, display):
+#     worker = DatabaseWorker(root_node=client)
+#     with qtbot.waitSignal(worker.distinct_fields_changed) as blocker:
+#         worker.load_distinct_fields()
+#     # Check that the dictionary has the right structure
+#     distinct_fields = blocker.args[0]
+#     for key in ["sample_name"]:
+#         assert key in distinct_fields.keys()
diff --git a/src/firefly/tests/test_voltmeters.py b/src/firefly/tests/test_voltmeters.py
index 272ef07a..f9a89d80 100644
--- a/src/firefly/tests/test_voltmeters.py
+++ b/src/firefly/tests/test_voltmeters.py
@@ -25,68 +25,68 @@ def test_device(qtbot, ffapp, I0):
     assert isinstance(display._device, haven.IonChamber)
 
 
-def test_scaler_prefix(qtbot, ffapp, sim_registry):
-    """Make sure the scaler prefix gets passed in as a macro."""
-    # Set up fake ion chamber
-    window = FireflyMainWindow()
-    ic = haven.IonChamber(
-        "",
-        scaler_prefix="255idcVME:scaler1",
-        ch_num=1,
-        name="my_ion_chamber",
-        labels={"ion_chambers"},
-    )
-    sim_registry.register(ic)
-    # Check the macros
-    display = VoltmetersDisplay()
-    assert display.macros()["SCALER"] == "255idcVME:scaler1"
-
-
-def test_embedded_display_widgets(qtbot, fake_ion_chambers, ffapp):
-    """Test the the voltmeters creates a new embedded display widget for
-    each ion chamber.
-
-    """
-    window = FireflyMainWindow()
-    # Load the display
-    vms_display = VoltmetersDisplay()
-    # Check that the embedded display widgets get added correctly
-    assert hasattr(vms_display, "_ion_chamber_displays")
-    assert len(vms_display._ion_chamber_displays) == 2
-    # two displays and a separator
-    assert vms_display.voltmeters_layout.count() == 3
-    # Check that the embedded display widgets have the correct macros
-    emb_disp = vms_display._ion_chamber_displays[0]
-    disp = emb_disp.open_file(force=True)
-    macros = disp.macros()
-    assert macros == {"IC": "I0", "SCALER": "scaler_ioc"}
-    # Check that a device has been created properly
-    assert isinstance(disp._device, haven.IonChamber)
-
-
-def test_ion_chamber_menu(fake_ion_chambers, qtbot, ffapp):
-    ffapp.setup_window_actions()
-    ffapp.setup_runengine_actions()
-    # Create the window
-    window = FireflyMainWindow()
-    # Check that the menu items have been created
-    assert hasattr(window.ui, "menuDetectors")
-    assert hasattr(window.ui, "menuIonChambers")
-    assert len(ffapp.ion_chamber_actions) == 2
-
-
-def test_open_ion_chamber_window(fake_ion_chambers, ffapp):
-    # Set up the application
-    ffapp.setup_window_actions()
-    ffapp.setup_runengine_actions()
-    # Simulate clicking on the menu action (they're in alpha order)
-    window = FireflyMainWindow()
-    action = ffapp.ion_chamber_actions["It"]
-    action.trigger()
-    # See if the window was created
-    ion_chamber_name = "FireflyMainWindow_ion_chamber_It"
-    assert ion_chamber_name in ffapp.windows.keys()
-    macros = ffapp.windows[ion_chamber_name].display_widget().macros()
-    assert macros["IC"] == "It"
-    # Clean up
-    window.close()
+# def test_scaler_prefix(qtbot, ffapp, sim_registry):
+#     """Make sure the scaler prefix gets passed in as a macro."""
+#     # Set up fake ion chamber
+#     window = FireflyMainWindow()
+#     ic = haven.IonChamber(
+#         "",
+#         scaler_prefix="255idcVME:scaler1",
+#         ch_num=1,
+#         name="my_ion_chamber",
+#         labels={"ion_chambers"},
+#     )
+#     sim_registry.register(ic)
+#     # Check the macros
+#     display = VoltmetersDisplay()
+#     assert display.macros()["SCALER"] == "255idcVME:scaler1"
+
+
+# def test_embedded_display_widgets(qtbot, fake_ion_chambers, ffapp):
+#     """Test the the voltmeters creates a new embedded display widget for
+#     each ion chamber.
+
+#     """
+#     window = FireflyMainWindow()
+#     # Load the display
+#     vms_display = VoltmetersDisplay()
+#     # Check that the embedded display widgets get added correctly
+#     assert hasattr(vms_display, "_ion_chamber_displays")
+#     assert len(vms_display._ion_chamber_displays) == 2
+#     # two displays and a separator
+#     assert vms_display.voltmeters_layout.count() == 3
+#     # Check that the embedded display widgets have the correct macros
+#     emb_disp = vms_display._ion_chamber_displays[0]
+#     disp = emb_disp.open_file(force=True)
+#     macros = disp.macros()
+#     assert macros == {"IC": "I0", "SCALER": "scaler_ioc"}
+#     # Check that a device has been created properly
+#     assert isinstance(disp._device, haven.IonChamber)
+
+
+# def test_ion_chamber_menu(fake_ion_chambers, qtbot, ffapp):
+#     ffapp.setup_window_actions()
+#     ffapp.setup_runengine_actions()
+#     # Create the window
+#     window = FireflyMainWindow()
+#     # Check that the menu items have been created
+#     assert hasattr(window.ui, "menuDetectors")
+#     assert hasattr(window.ui, "menuIonChambers")
+#     assert len(ffapp.ion_chamber_actions) == 2
+
+
+# def test_open_ion_chamber_window(fake_ion_chambers, ffapp):
+#     # Set up the application
+#     ffapp.setup_window_actions()
+#     ffapp.setup_runengine_actions()
+#     # Simulate clicking on the menu action (they're in alpha order)
+#     window = FireflyMainWindow()
+#     action = ffapp.ion_chamber_actions["It"]
+#     action.trigger()
+#     # See if the window was created
+#     ion_chamber_name = "FireflyMainWindow_ion_chamber_It"
+#     assert ion_chamber_name in ffapp.windows.keys()
+#     macros = ffapp.windows[ion_chamber_name].display_widget().macros()
+#     assert macros["IC"] == "It"
+#     # Clean up
+#     window.close()
diff --git a/src/firefly/tests/test_xrf_detector_display.py b/src/firefly/tests/test_xrf_detector_display.py
index 1e6c7139..e256d355 100644
--- a/src/firefly/tests/test_xrf_detector_display.py
+++ b/src/firefly/tests/test_xrf_detector_display.py
@@ -88,233 +88,233 @@ def test_all_rois_selection(xrf_display):
     assert not xrf_display.roi_displays[1].isEnabled()
 
 
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_all_mcas_selection(xrf_display):
-    """Are all the other ROIs disabled when one is selected?"""
-    mca_display = xrf_display.mca_displays[0]
-    # Pretend an ROI display was selected
-    mca_display.selected.emit(True)
-    # Check that a different ROI display was disabled
-    assert not xrf_display.mca_displays[1].isEnabled()
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_update_roi_spectra(qtbot, xrf_display):
-    spectra = np.random.default_rng(seed=0).integers(
-        0, 65536, dtype=np.int_, size=(4, 1024)
-    )
-    roi_plot_widget = xrf_display.ui.roi_plot_widget
-    with qtbot.waitSignal(roi_plot_widget.plot_changed):
-        xrf_display._spectrum_channels[0].value_slot(spectra[0])
-        xrf_display._spectrum_channels[1].value_slot(spectra[1])
-    # Check that a PlotItem was created
-    plot_item = roi_plot_widget.ui.plot_widget.getPlotItem()
-    assert isinstance(plot_item, PlotItem)
-    # Check that the spectrum was plotted
-    data_items = plot_item.listDataItems()
-    assert len(data_items) == 1
-    # Check that previous plots get cleared
-    spectra2 = np.random.default_rng(seed=1).integers(
-        0, 65536, dtype=np.int_, size=(4, 1024)
-    )
-    with qtbot.waitSignal(roi_plot_widget.plot_changed):
-        xrf_display._spectrum_channels[0].value_slot(spectra2[0])
-    data_items = plot_item.listDataItems()
-    assert len(data_items) == 1
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_update_mca_spectra(xrf_display, qtbot):
-    spectra = np.random.default_rng(seed=0).integers(
-        0, 65536, dtype=np.int_, size=(4, 1024)
-    )
-    mca_plot_widget = xrf_display.ui.mca_plot_widget
-    # Check that a PlotItem was created in the fixture
-    plot_item = mca_plot_widget.ui.plot_widget.getPlotItem()
-    assert isinstance(plot_item, PlotItem)
-    # Clear the data items so we can test them later
-    plot_item.clear()
-    # plot_widget.update_spectrum(spectrum=spectra[0], mca_idx=1)
-    with qtbot.waitSignal(mca_plot_widget.plot_changed):
-        xrf_display._spectrum_channels[0].value_slot(spectra[0])
-        xrf_display._spectrum_channels[1].value_slot(spectra[1])
-    # Check that the spectrum was plotted
-    data_items = plot_item.listDataItems()
-    assert len(data_items) == 2
-    # Check that previous plots get cleared
-    spectra2 = np.random.default_rng(seed=1).integers(
-        0, 65536, dtype=np.int_, size=(4, 1024)
-    )
-    with qtbot.waitSignal(mca_plot_widget.plot_changed):
-        xrf_display._spectrum_channels[0].value_slot(spectra2[0])
-    data_items = plot_item.listDataItems()
-    assert len(data_items) == 2
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_mca_selected_highlights(qtbot, xrf_display):
-    """Is the spectrum highlighted when the element row is selected."""
-    mca_display = xrf_display.mca_displays[1]
-    spectra = np.random.default_rng(seed=0).integers(
-        0, 65536, dtype=np.int_, size=(4, 1024)
-    )
-    plot_widget = xrf_display.mca_plot_widget
-    plot_widget.update_spectrum(0, spectra[0])
-    plot_widget.update_spectrum(1, spectra[1])
-    this_data_item = xrf_display.mca_plot_widget._data_items[1]
-    other_data_item = xrf_display.mca_plot_widget._data_items[0]
-    this_data_item.setOpacity(0.77)
-    # Select this display and check if the spectrum is highlighted
-    mca_display.selected.emit(True)
-    assert this_data_item.opacity() == 1.0
-    assert other_data_item.opacity() == 0.15
-    # Hovering other rows should not affect the opacity
-    xrf_display.mca_displays[0].enterEvent()
-    assert this_data_item.opacity() == 0.55
-    assert other_data_item.opacity() == 1.0
-    xrf_display.mca_displays[0].leaveEvent()
-    assert this_data_item.opacity() == 1.0
-    assert other_data_item.opacity() == 0.15
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_show_mca_region_visibility(xrf_display):
-    """Is the spectrum highlighted when the element row is selected."""
-    # Check that the region is hidden at startup
-    plot_widget = xrf_display.mca_plot_widget
-    region = plot_widget.region(mca_num=2, roi_num=0)
-    assert not region.isVisible()
-    # Now highlight a spectrum, and confirm it is visible
-    plot_widget.highlight_spectrum(mca_num=2, roi_num=0, hovered=True)
-    assert region.isVisible()
-    # assert region.brush.color().name() == "#ff7f0e"
-    # Unhighlight and confirm it is invisible
-    plot_widget.highlight_spectrum(mca_num=1, roi_num=0, hovered=False)
-    assert not region.isVisible()
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_show_roi_region(xrf_display):
-    """Is the spectrum highlighted when the element row is selected."""
-    # Check that the region is hidden at startup
-    plot_widget = xrf_display.roi_plot_widget
-    hovered_region = plot_widget.region(mca_num=1, roi_num=1)
-    plot_widget.select_roi(mca_num=1, roi_num=0, is_selected=True)
-    selected_region = plot_widget.region(mca_num=1, roi_num=0)
-    assert not hovered_region.isVisible()
-    assert selected_region.isVisible()
-    # Now highlight a spectrum, and confirm it is visible
-    plot_widget.highlight_spectrum(mca_num=1, roi_num=1, hovered=True)
-    assert hovered_region.isVisible()
-    assert not selected_region.isVisible()
-    # assert region.brush.color().name() == "#ff7f0e"
-    # Unhighlight and confirm it is invisible
-    plot_widget.highlight_spectrum(mca_num=1, roi_num=0, hovered=False)
-    assert not hovered_region.isVisible()
-    assert selected_region.isVisible()
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_mca_region_channels(xrf_display):
-    """Are the channel access connections between the ROI selection region
-    and the hi/lo channel PVs correct?
-
-    """
-    plot_widget = xrf_display.mca_plot_widget
-    plot_widget.device_name = "vortex_me4"
-    mca_display = xrf_display.mca_displays[1]
-    mca_display._embedded_widget = mca_display.open_file(force=True)
-    xrf_display.mca_selected(is_selected=True, mca_num=2)
-    correct_address = "sig://vortex_me4.mcas.mca2.rois.roi0.hi_chan"
-    region = plot_widget.region(mca_num=2, roi_num=0)
-    assert region.hi_channel.address == correct_address
-    region.hi_channel.value_slot(108)
-    assert region.getRegion()[1] == 108
-    region.lo_channel.value_slot(47)
-    assert region.getRegion()[0] == 47
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_mca_copyall_button(xrf_display, qtbot):
-    xrf_display.mca_selected(is_selected=True, mca_num=1)
-    assert xrf_display.ui.mca_copyall_button.isEnabled()
-    # Set up ROI displays to test
-    this_display = xrf_display.mca_displays[1]
-    this_display._embedded_widget = this_display.open_file(force=True)
-    other_display = xrf_display.mca_displays[0]
-    other_display._embedded_widget = other_display.open_file(force=True)
-    # Change the values on the MCA displays
-    this_display.embedded_widget.ui.lower_lineedit.setText("111")
-    this_display.embedded_widget.ui.upper_lineedit.setText("131")
-    this_display.embedded_widget.ui.label_lineedit.setText("Ni Ka")
-    # Copy to the other MCA display
-    qtbot.mouseClick(xrf_display.ui.mca_copyall_button, QtCore.Qt.LeftButton)
-    assert other_display.embedded_widget.ui.lower_lineedit.text() == "111"
-    assert other_display.embedded_widget.ui.upper_lineedit.text() == "131"
-    # Does the button get disabled on un-select?
-    xrf_display.mca_selected(is_selected=False, mca_num=2)
-    assert not xrf_display.ui.mca_copyall_button.isEnabled()
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_roi_copyall_button(xrf_display, qtbot):
-    # Set up ROI rows embedded display widgets
-    for disp in xrf_display.roi_displays:
-        disp._embedded_widget = disp.open_file(force=True)
-    # Select an ROI
-    xrf_display.roi_selected(is_selected=True, roi_num=1)
-    assert xrf_display.ui.roi_copyall_button.isEnabled()
-    # Set up ROI displays to test
-    this_display = xrf_display.roi_displays[1]
-    this_display._embedded_widget = this_display.open_file(force=True)
-    other_display = xrf_display.roi_displays[0]
-    other_display._embedded_widget = other_display.open_file(force=True)
-    # Change the values on the MCA displays
-    this_display.embedded_widget.ui.lower_lineedit.setText("111")
-    this_display.embedded_widget.ui.upper_lineedit.setText("131")
-    this_display.embedded_widget.ui.label_lineedit.setText("Ni Ka")
-    # Copy to the other ROI display
-    qtbot.mouseClick(xrf_display.ui.roi_copyall_button, QtCore.Qt.LeftButton)
-    assert other_display.embedded_widget.ui.lower_lineedit.text() == "111"
-    assert other_display.embedded_widget.ui.upper_lineedit.text() == "131"
-    # Does the button get disabled on un-select?
-    xrf_display.roi_selected(is_selected=False, roi_num=1)
-    assert not xrf_display.ui.roi_copyall_button.isEnabled()
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_mca_enableall_checkbox(xrf_display):
-    checkbox = xrf_display.ui.mca_enableall_checkbox
-    assert checkbox.checkState() == QtCore.Qt.PartiallyChecked
-    assert checkbox.isTristate()
-    for display in xrf_display.mca_displays:
-        display._embedded_widget = display.open_file(force=True)
-        assert not display.embedded_widget.ui.enabled_checkbox.checkState()
-    # Set it to checked and make sure all the ROI checkboxes respond
-    checkbox.setCheckState(QtCore.Qt.Checked)
-    assert not checkbox.isTristate()
-    for display in xrf_display.mca_displays:
-        assert display.embedded_widget.ui.enabled_checkbox.isChecked()
-    # Un-enable all, does it go back?
-    checkbox.setCheckState(QtCore.Qt.Unchecked)
-    for display in xrf_display.mca_displays:
-        assert not display.embedded_widget.ui.enabled_checkbox.isChecked()
-
-
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-def test_roi_enableall_checkbox(xrf_display):
-    checkbox = xrf_display.ui.roi_enableall_checkbox
-    assert checkbox.checkState() == QtCore.Qt.PartiallyChecked
-    assert checkbox.isTristate()
-    for display in xrf_display.roi_displays:
-        display._embedded_widget = display.open_file(force=True)
-        assert not display.embedded_widget.ui.enabled_checkbox.checkState()
-    # Set it to checked and make sure all the ROI checkboxes respond
-    checkbox.setCheckState(QtCore.Qt.Checked)
-    assert not checkbox.isTristate()
-    for display in xrf_display.roi_displays:
-        assert display.embedded_widget.ui.enabled_checkbox.isChecked()
-    # Un-enable all, does it go back?
-    checkbox.setCheckState(QtCore.Qt.Unchecked)
-    for display in xrf_display.roi_displays:
-        assert not display.embedded_widget.ui.enabled_checkbox.isChecked()
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_all_mcas_selection(xrf_display):
+#     """Are all the other ROIs disabled when one is selected?"""
+#     mca_display = xrf_display.mca_displays[0]
+#     # Pretend an ROI display was selected
+#     mca_display.selected.emit(True)
+#     # Check that a different ROI display was disabled
+#     assert not xrf_display.mca_displays[1].isEnabled()
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_update_roi_spectra(qtbot, xrf_display):
+#     spectra = np.random.default_rng(seed=0).integers(
+#         0, 65536, dtype=np.int_, size=(4, 1024)
+#     )
+#     roi_plot_widget = xrf_display.ui.roi_plot_widget
+#     with qtbot.waitSignal(roi_plot_widget.plot_changed):
+#         xrf_display._spectrum_channels[0].value_slot(spectra[0])
+#         xrf_display._spectrum_channels[1].value_slot(spectra[1])
+#     # Check that a PlotItem was created
+#     plot_item = roi_plot_widget.ui.plot_widget.getPlotItem()
+#     assert isinstance(plot_item, PlotItem)
+#     # Check that the spectrum was plotted
+#     data_items = plot_item.listDataItems()
+#     assert len(data_items) == 1
+#     # Check that previous plots get cleared
+#     spectra2 = np.random.default_rng(seed=1).integers(
+#         0, 65536, dtype=np.int_, size=(4, 1024)
+#     )
+#     with qtbot.waitSignal(roi_plot_widget.plot_changed):
+#         xrf_display._spectrum_channels[0].value_slot(spectra2[0])
+#     data_items = plot_item.listDataItems()
+#     assert len(data_items) == 1
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_update_mca_spectra(xrf_display, qtbot):
+#     spectra = np.random.default_rng(seed=0).integers(
+#         0, 65536, dtype=np.int_, size=(4, 1024)
+#     )
+#     mca_plot_widget = xrf_display.ui.mca_plot_widget
+#     # Check that a PlotItem was created in the fixture
+#     plot_item = mca_plot_widget.ui.plot_widget.getPlotItem()
+#     assert isinstance(plot_item, PlotItem)
+#     # Clear the data items so we can test them later
+#     plot_item.clear()
+#     # plot_widget.update_spectrum(spectrum=spectra[0], mca_idx=1)
+#     with qtbot.waitSignal(mca_plot_widget.plot_changed):
+#         xrf_display._spectrum_channels[0].value_slot(spectra[0])
+#         xrf_display._spectrum_channels[1].value_slot(spectra[1])
+#     # Check that the spectrum was plotted
+#     data_items = plot_item.listDataItems()
+#     assert len(data_items) == 2
+#     # Check that previous plots get cleared
+#     spectra2 = np.random.default_rng(seed=1).integers(
+#         0, 65536, dtype=np.int_, size=(4, 1024)
+#     )
+#     with qtbot.waitSignal(mca_plot_widget.plot_changed):
+#         xrf_display._spectrum_channels[0].value_slot(spectra2[0])
+#     data_items = plot_item.listDataItems()
+#     assert len(data_items) == 2
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_mca_selected_highlights(qtbot, xrf_display):
+#     """Is the spectrum highlighted when the element row is selected."""
+#     mca_display = xrf_display.mca_displays[1]
+#     spectra = np.random.default_rng(seed=0).integers(
+#         0, 65536, dtype=np.int_, size=(4, 1024)
+#     )
+#     plot_widget = xrf_display.mca_plot_widget
+#     plot_widget.update_spectrum(0, spectra[0])
+#     plot_widget.update_spectrum(1, spectra[1])
+#     this_data_item = xrf_display.mca_plot_widget._data_items[1]
+#     other_data_item = xrf_display.mca_plot_widget._data_items[0]
+#     this_data_item.setOpacity(0.77)
+#     # Select this display and check if the spectrum is highlighted
+#     mca_display.selected.emit(True)
+#     assert this_data_item.opacity() == 1.0
+#     assert other_data_item.opacity() == 0.15
+#     # Hovering other rows should not affect the opacity
+#     xrf_display.mca_displays[0].enterEvent()
+#     assert this_data_item.opacity() == 0.55
+#     assert other_data_item.opacity() == 1.0
+#     xrf_display.mca_displays[0].leaveEvent()
+#     assert this_data_item.opacity() == 1.0
+#     assert other_data_item.opacity() == 0.15
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_show_mca_region_visibility(xrf_display):
+#     """Is the spectrum highlighted when the element row is selected."""
+#     # Check that the region is hidden at startup
+#     plot_widget = xrf_display.mca_plot_widget
+#     region = plot_widget.region(mca_num=2, roi_num=0)
+#     assert not region.isVisible()
+#     # Now highlight a spectrum, and confirm it is visible
+#     plot_widget.highlight_spectrum(mca_num=2, roi_num=0, hovered=True)
+#     assert region.isVisible()
+#     # assert region.brush.color().name() == "#ff7f0e"
+#     # Unhighlight and confirm it is invisible
+#     plot_widget.highlight_spectrum(mca_num=1, roi_num=0, hovered=False)
+#     assert not region.isVisible()
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_show_roi_region(xrf_display):
+#     """Is the spectrum highlighted when the element row is selected."""
+#     # Check that the region is hidden at startup
+#     plot_widget = xrf_display.roi_plot_widget
+#     hovered_region = plot_widget.region(mca_num=1, roi_num=1)
+#     plot_widget.select_roi(mca_num=1, roi_num=0, is_selected=True)
+#     selected_region = plot_widget.region(mca_num=1, roi_num=0)
+#     assert not hovered_region.isVisible()
+#     assert selected_region.isVisible()
+#     # Now highlight a spectrum, and confirm it is visible
+#     plot_widget.highlight_spectrum(mca_num=1, roi_num=1, hovered=True)
+#     assert hovered_region.isVisible()
+#     assert not selected_region.isVisible()
+#     # assert region.brush.color().name() == "#ff7f0e"
+#     # Unhighlight and confirm it is invisible
+#     plot_widget.highlight_spectrum(mca_num=1, roi_num=0, hovered=False)
+#     assert not hovered_region.isVisible()
+#     assert selected_region.isVisible()
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_mca_region_channels(xrf_display):
+#     """Are the channel access connections between the ROI selection region
+#     and the hi/lo channel PVs correct?
+
+#     """
+#     plot_widget = xrf_display.mca_plot_widget
+#     plot_widget.device_name = "vortex_me4"
+#     mca_display = xrf_display.mca_displays[1]
+#     mca_display._embedded_widget = mca_display.open_file(force=True)
+#     xrf_display.mca_selected(is_selected=True, mca_num=2)
+#     correct_address = "sig://vortex_me4.mcas.mca2.rois.roi0.hi_chan"
+#     region = plot_widget.region(mca_num=2, roi_num=0)
+#     assert region.hi_channel.address == correct_address
+#     region.hi_channel.value_slot(108)
+#     assert region.getRegion()[1] == 108
+#     region.lo_channel.value_slot(47)
+#     assert region.getRegion()[0] == 47
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_mca_copyall_button(xrf_display, qtbot):
+#     xrf_display.mca_selected(is_selected=True, mca_num=1)
+#     assert xrf_display.ui.mca_copyall_button.isEnabled()
+#     # Set up ROI displays to test
+#     this_display = xrf_display.mca_displays[1]
+#     this_display._embedded_widget = this_display.open_file(force=True)
+#     other_display = xrf_display.mca_displays[0]
+#     other_display._embedded_widget = other_display.open_file(force=True)
+#     # Change the values on the MCA displays
+#     this_display.embedded_widget.ui.lower_lineedit.setText("111")
+#     this_display.embedded_widget.ui.upper_lineedit.setText("131")
+#     this_display.embedded_widget.ui.label_lineedit.setText("Ni Ka")
+#     # Copy to the other MCA display
+#     qtbot.mouseClick(xrf_display.ui.mca_copyall_button, QtCore.Qt.LeftButton)
+#     assert other_display.embedded_widget.ui.lower_lineedit.text() == "111"
+#     assert other_display.embedded_widget.ui.upper_lineedit.text() == "131"
+#     # Does the button get disabled on un-select?
+#     xrf_display.mca_selected(is_selected=False, mca_num=2)
+#     assert not xrf_display.ui.mca_copyall_button.isEnabled()
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_roi_copyall_button(xrf_display, qtbot):
+#     # Set up ROI rows embedded display widgets
+#     for disp in xrf_display.roi_displays:
+#         disp._embedded_widget = disp.open_file(force=True)
+#     # Select an ROI
+#     xrf_display.roi_selected(is_selected=True, roi_num=1)
+#     assert xrf_display.ui.roi_copyall_button.isEnabled()
+#     # Set up ROI displays to test
+#     this_display = xrf_display.roi_displays[1]
+#     this_display._embedded_widget = this_display.open_file(force=True)
+#     other_display = xrf_display.roi_displays[0]
+#     other_display._embedded_widget = other_display.open_file(force=True)
+#     # Change the values on the MCA displays
+#     this_display.embedded_widget.ui.lower_lineedit.setText("111")
+#     this_display.embedded_widget.ui.upper_lineedit.setText("131")
+#     this_display.embedded_widget.ui.label_lineedit.setText("Ni Ka")
+#     # Copy to the other ROI display
+#     qtbot.mouseClick(xrf_display.ui.roi_copyall_button, QtCore.Qt.LeftButton)
+#     assert other_display.embedded_widget.ui.lower_lineedit.text() == "111"
+#     assert other_display.embedded_widget.ui.upper_lineedit.text() == "131"
+#     # Does the button get disabled on un-select?
+#     xrf_display.roi_selected(is_selected=False, roi_num=1)
+#     assert not xrf_display.ui.roi_copyall_button.isEnabled()
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_mca_enableall_checkbox(xrf_display):
+#     checkbox = xrf_display.ui.mca_enableall_checkbox
+#     assert checkbox.checkState() == QtCore.Qt.PartiallyChecked
+#     assert checkbox.isTristate()
+#     for display in xrf_display.mca_displays:
+#         display._embedded_widget = display.open_file(force=True)
+#         assert not display.embedded_widget.ui.enabled_checkbox.checkState()
+#     # Set it to checked and make sure all the ROI checkboxes respond
+#     checkbox.setCheckState(QtCore.Qt.Checked)
+#     assert not checkbox.isTristate()
+#     for display in xrf_display.mca_displays:
+#         assert display.embedded_widget.ui.enabled_checkbox.isChecked()
+#     # Un-enable all, does it go back?
+#     checkbox.setCheckState(QtCore.Qt.Unchecked)
+#     for display in xrf_display.mca_displays:
+#         assert not display.embedded_widget.ui.enabled_checkbox.isChecked()
+
+
+# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+# def test_roi_enableall_checkbox(xrf_display):
+#     checkbox = xrf_display.ui.roi_enableall_checkbox
+#     assert checkbox.checkState() == QtCore.Qt.PartiallyChecked
+#     assert checkbox.isTristate()
+#     for display in xrf_display.roi_displays:
+#         display._embedded_widget = display.open_file(force=True)
+#         assert not display.embedded_widget.ui.enabled_checkbox.checkState()
+#     # Set it to checked and make sure all the ROI checkboxes respond
+#     checkbox.setCheckState(QtCore.Qt.Checked)
+#     assert not checkbox.isTristate()
+#     for display in xrf_display.roi_displays:
+#         assert display.embedded_widget.ui.enabled_checkbox.isChecked()
+#     # Un-enable all, does it go back?
+#     checkbox.setCheckState(QtCore.Qt.Unchecked)
+#     for display in xrf_display.roi_displays:
+#         assert not display.embedded_widget.ui.enabled_checkbox.isChecked()

From 61886516344f74a76a8b1fdc258722fb171ca56f Mon Sep 17 00:00:00 2001
From: Mark Wolfman <canismarko@gmail.com>
Date: Wed, 15 Nov 2023 11:42:20 -0600
Subject: [PATCH 4/9] Attempted fix for the CI seg faults.

Seems to work after we switch to xvfb-run and set runs-on to
ubuntu-20.04
---
 .github/workflows/ci.yml                      |  8 ++--
 src/firefly/application.py                    |  3 --
 .../tests/test_xrf_detector_display.py        | 15 +++---
 src/haven/instrument/xspress.py               | 48 +++++++++----------
 4 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ce235adb..6d835da7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ jobs:
     defaults:
       run:
         shell: bash -l {0}
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-20.04
     strategy:
       max-parallel: 5
     steps:
@@ -29,8 +29,8 @@ jobs:
       uses: mamba-org/setup-micromamba@v1
       with:
         environment-file: environment.yml
-    - name: Setup X virtual frame buffer
-      run: Xvfb $DISPLAY &
+    # - name: Setup X virtual frame buffer
+    #   run: Xvfb $DISPLAY &
     - name: Install haven
       run: pip install -e .
     - name: Environment info
@@ -45,4 +45,4 @@ jobs:
         # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
     - name: Test with pytest
-      run: pytest --timeout=120 src/firefly/tests/test_run_browser.py src/firefly/tests/test_voltmeters.py src/firefly/tests/test_xrf_detector_display.py
+      run: xvfb-run python -m pytest -vv --timeout=120 src/firefly/tests/test_run_browser.py src/firefly/tests/test_voltmeters.py src/firefly/tests/test_xrf_detector_display.py
diff --git a/src/firefly/application.py b/src/firefly/application.py
index 56d106bf..a8846cac 100644
--- a/src/firefly/application.py
+++ b/src/firefly/application.py
@@ -265,10 +265,7 @@ def _prepare_device_windows(self, device_label: str, attr_name: str):
         window_slots = []
         setattr(self, f"{attr_name}_window_slots", window_slots)
         setattr(self, f"{attr_name}_windows", {})
-        # if attr_name == "ion_chamber":
-        #     breakpoint()
         for device in devices:
-            # Check that we don't already have actions set
             # Create the window action
             action = QtWidgets.QAction(self)
             action.setObjectName(f"actionShow_{attr_name}_{device.name}")
diff --git a/src/firefly/tests/test_xrf_detector_display.py b/src/firefly/tests/test_xrf_detector_display.py
index e256d355..e90f1496 100644
--- a/src/firefly/tests/test_xrf_detector_display.py
+++ b/src/firefly/tests/test_xrf_detector_display.py
@@ -10,6 +10,9 @@
 from firefly.xrf_roi import XRFROIDisplay
 
 
+detectors = ['xspress',]
+
+
 @pytest.fixture()
 def xrf_display(ffapp, request):
     """Parameterized fixture for creating a display based on a specific
@@ -29,10 +32,10 @@ def xrf_display(ffapp, request):
     plot_widget.update_spectrum(1, spectra[1])
     plot_widget.update_spectrum(2, spectra[2])
     plot_widget.update_spectrum(3, spectra[3])
-    return display
+    yield display
 
 
-@pytest.mark.parametrize("det_fixture", ["dxp", "xspress"])
+@pytest.mark.parametrize("det_fixture", detectors)
 def test_open_xrf_detector_viewer_actions(ffapp, qtbot, det_fixture, request):
     sim_det = request.getfixturevalue(det_fixture)
     # Get the area detector parts ready
@@ -44,7 +47,7 @@ def test_open_xrf_detector_viewer_actions(ffapp, qtbot, det_fixture, request):
     assert "FireflyMainWindow_xrf_detector_vortex_me4" in ffapp.windows.keys()
 
 
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
 def test_roi_widgets(xrf_display):
     xrf_display.draw_roi_widgets(2)
     # Check that the widgets were drawn
@@ -52,7 +55,7 @@ def test_roi_widgets(xrf_display):
     disp = xrf_display.roi_displays[0]
 
 
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
 def test_roi_element_comboboxes(ffapp, qtbot, xrf_display):
     # Check that the comboboxes have the right number of entries
     element_cb = xrf_display.ui.mca_combobox
@@ -61,7 +64,7 @@ def test_roi_element_comboboxes(ffapp, qtbot, xrf_display):
     assert roi_cb.count() == xrf_display.device.num_rois
 
 
-@pytest.mark.parametrize("det_fixture", ["dxp", "xspress"])
+@pytest.mark.parametrize("det_fixture", detectors)
 def test_roi_selection(ffapp, qtbot, det_fixture, request):
     det = request.getfixturevalue(det_fixture)
     display = XRFROIDisplay(macros={"DEV": det.name, "NUM": 2, "MCA": 2, "ROI": 2})
@@ -78,7 +81,7 @@ def test_roi_selection(ffapp, qtbot, det_fixture, request):
     assert f"background: {display.selected_background}" not in display.styleSheet()
 
 
-@pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
 def test_all_rois_selection(xrf_display):
     """Are all the other ROIs disabled when one is selected?"""
     roi_display = xrf_display.roi_displays[0]
diff --git a/src/haven/instrument/xspress.py b/src/haven/instrument/xspress.py
index 3dea0ca2..dc63e9b4 100644
--- a/src/haven/instrument/xspress.py
+++ b/src/haven/instrument/xspress.py
@@ -81,30 +81,30 @@
 class ChannelSignal(MultiDerivedSignal):
     """A high/low range limit channel for an ROI."""
 
-    def set(
-        self,
-        value: OphydDataType,
-        *,
-        timeout: Optional[float] = None,
-        settle_time: Optional[float] = None,
-    ) -> StatusBase:
-        # Check for existing signals and, if necessary, wait them out
-        signals = [
-            self.parent.hi_chan,
-            self.parent.lo_chan,
-            self.parent.size,
-            self.parent._lo_chan,
-        ]
-
-        def get_threads():
-            thds = [sig._set_thread for sig in signals if sig._set_thread]
-            return [th for th in thds if th is not None]
-
-        while len(threads := get_threads()) > 0:
-            for th in threads:
-                th.join()
-        # Set the signal like normal
-        return super().set(value, timeout=timeout, settle_time=settle_time)
+    # def set(
+    #     self,
+    #     value: OphydDataType,
+    #     *,
+    #     timeout: Optional[float] = None,
+    #     settle_time: Optional[float] = None,
+    # ) -> StatusBase:
+    #     # Check for existing signals and, if necessary, wait them out
+    #     signals = [
+    #         self.parent.hi_chan,
+    #         self.parent.lo_chan,
+    #         self.parent.size,
+    #         self.parent._lo_chan,
+    #     ]
+
+    #     def get_threads():
+    #         thds = [sig._set_thread for sig in signals if sig._set_thread]
+    #         return [th for th in thds if th is not None]
+
+    #     while len(threads := get_threads()) > 0:
+    #         for th in threads:
+    #             th.join()
+    #     # Set the signal like normal
+    #     return super().set(value, timeout=timeout, settle_time=settle_time)
 
 
 class ROI(ROIMixin):

From 7b260566e498e2b53e452afaf0d4c09eeb7db258 Mon Sep 17 00:00:00 2001
From: Mark Wolfman <canismarko@gmail.com>
Date: Wed, 15 Nov 2023 14:29:53 -0600
Subject: [PATCH 5/9] CI now runs all the tests again.

---
 .github/workflows/ci.yml                      |   6 +-
 src/firefly/tests/test_run_browser.py         | 410 ++++++++--------
 src/firefly/tests/test_voltmeters.py          | 130 ++---
 .../tests/test_xrf_detector_display.py        | 462 +++++++++---------
 src/haven/instrument/xspress.py               |  48 +-
 5 files changed, 527 insertions(+), 529 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6d835da7..aa30fb63 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,8 +29,6 @@ jobs:
       uses: mamba-org/setup-micromamba@v1
       with:
         environment-file: environment.yml
-    # - name: Setup X virtual frame buffer
-    #   run: Xvfb $DISPLAY &
     - name: Install haven
       run: pip install -e .
     - name: Environment info
@@ -44,5 +42,5 @@ jobs:
         flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
         # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
-    - name: Test with pytest
-      run: xvfb-run python -m pytest -vv --timeout=120 src/firefly/tests/test_run_browser.py src/firefly/tests/test_voltmeters.py src/firefly/tests/test_xrf_detector_display.py
+    - name: Test with pytest in Xvfb
+      run: xvfb-run python -m pytest -vv --timeout=120
diff --git a/src/firefly/tests/test_run_browser.py b/src/firefly/tests/test_run_browser.py
index 0917ad73..a4cc6a5f 100644
--- a/src/firefly/tests/test_run_browser.py
+++ b/src/firefly/tests/test_run_browser.py
@@ -106,16 +106,16 @@ def display(ffapp, client, qtbot):
         assert not display._thread.isRunning()
 
 
-# def test_client_fixture(client):
-#     """Does the client fixture load without stalling the test runner?"""
-#     pass
+def test_client_fixture(client):
+    """Does the client fixture load without stalling the test runner?"""
+    pass
 
 
-# def test_run_viewer_action(ffapp, monkeypatch):
-#     monkeypatch.setattr(ffapp, "create_window", MagicMock())
-#     assert hasattr(ffapp, "show_run_browser_action")
-#     ffapp.show_run_browser_action.trigger()
-#     assert isinstance(ffapp.windows["run_browser"], MagicMock)
+def test_run_viewer_action(ffapp, monkeypatch):
+    monkeypatch.setattr(ffapp, "create_window", MagicMock())
+    assert hasattr(ffapp, "show_run_browser_action")
+    ffapp.show_run_browser_action.trigger()
+    assert isinstance(ffapp.windows["run_browser"], MagicMock)
 
 
 def test_load_runs(display):
@@ -123,200 +123,200 @@ def test_load_runs(display):
     assert display.ui.runs_total_label.text() == str(display.runs_model.rowCount())
 
 
-# def test_update_selected_runs(qtbot, display):
-#     # Change the proposal item
-#     selection_model = display.ui.run_tableview.selectionModel()
-#     item = display.runs_model.item(0, 1)
-#     assert item is not None
-#     rect = display.run_tableview.visualRect(item.index())
-#     with qtbot.waitSignal(display._db_worker.selected_runs_changed):
-#         qtbot.mouseClick(
-#             display.run_tableview.viewport(), Qt.LeftButton, pos=rect.center()
-#         )
-#     # Check that the runs were saved
-#     assert len(display._db_worker.selected_runs) > 0
-
-
-# def test_metadata(qtbot, display):
-#     # Change the proposal item
-#     selection_model = display.ui.run_tableview.selectionModel()
-#     item = display.runs_model.item(0, 1)
-#     assert item is not None
-#     rect = display.run_tableview.visualRect(item.index())
-#     with qtbot.waitSignal(display._db_worker.selected_runs_changed):
-#         qtbot.mouseClick(
-#             display.run_tableview.viewport(), Qt.LeftButton, pos=rect.center()
-#         )
-#     # Check that the metadata was set properly in the Metadata tab
-#     metadata_doc = display.ui.metadata_textedit.document()
-#     text = display.ui.metadata_textedit.document().toPlainText()
-#     assert "xafs_scan" in text
-
-
-# def test_1d_plot_signals(client, display):
-#     # Check that the 1D plot was created
-#     plot_widget = display.ui.plot_1d_view
-#     plot_item = display.plot_1d_item
-#     assert isinstance(plot_widget, PlotWidget)
-#     assert isinstance(plot_item, PlotItem)
-#     # Update the list of runs and see if the controsl get updated
-#     display._db_worker.selected_runs = client.values()
-#     display._db_worker.selected_runs_changed.emit([])
-#     # Check signals in checkboxes
-#     for combobox in [
-#         display.ui.multi_signal_x_combobox,
-#         display.ui.signal_y_combobox,
-#         display.ui.signal_r_combobox,
-#         display.ui.signal_x_combobox,
-#     ]:
-#         assert (
-#             combobox.findText("energy_energy") > -1
-#         ), f"energy_energy signal not in {combobox.objectName()}."
-
-
-# def test_1d_plot_signal_memory(client, display):
-#     """Do we remember the signals that were previously selected."""
-#     # Check that the 1D plot was created
-#     plot_widget = display.ui.plot_1d_view
-#     plot_item = display.plot_1d_item
-#     assert isinstance(plot_widget, PlotWidget)
-#     assert isinstance(plot_item, PlotItem)
-#     # Update the list of runs and see if the controls get updated
-#     display._db_worker.selected_runs = client.values()
-#     display.update_1d_signals()
-#     # Check signals in comboboxes
-#     cb = display.ui.signal_y_combobox
-#     assert cb.currentText() == "energy_energy"
-#     cb.setCurrentIndex(1)
-#     assert cb.currentText() == "energy_id_energy_readback"
-#     # Update the combobox signals and make sure the text didn't change
-#     display.update_1d_signals()
-#     assert cb.currentText() == "energy_id_energy_readback"
-
-
-# def test_1d_hinted_signals(client, display):
-#     display.ui.plot_1d_hints_checkbox.setChecked(True)
-#     # Check that the 1D plot was created
-#     plot_widget = display.ui.plot_1d_view
-#     plot_item = display.plot_1d_item
-#     assert isinstance(plot_widget, PlotWidget)
-#     assert isinstance(plot_item, PlotItem)
-#     # Update the list of runs and see if the controsl get updated
-#     display._db_worker.selected_runs = client.values()
-#     display.update_1d_signals()
-#     # Check signals in checkboxes
-#     combobox = display.ui.signal_x_combobox
-#     assert (
-#         combobox.findText("energy_energy") > -1
-#     ), f"hinted signal not in {combobox.objectName()}."
-#     assert (
-#         combobox.findText("It_net_counts") == -1
-#     ), f"unhinted signal found in {combobox.objectName()}."
-
-
-# @pytest.mark.skip(reason="Need to figure out why tiled fails with this test.")
-# def test_update_1d_plot(client, display, qtbot):
-#     run = client.values()[0]
-#     run_data = run["primary"]["data"].read()
-#     expected_xdata = run_data.energy_energy
-#     expected_ydata = np.log(run_data.I0_net_counts / run_data.It_net_counts)
-#     expected_ydata = np.gradient(expected_ydata, expected_xdata)
-#     with qtbot.waitSignal(display.plot_1d_changed):
-#         display._db_worker.selected_runs_changed.emit([])
-#     # Set the controls to describe the data we want to test
-#     x_combobox = display.ui.signal_x_combobox
-#     x_combobox.addItem("energy_energy")
-#     x_combobox.setCurrentText("energy_energy")
-#     y_combobox = display.ui.signal_y_combobox
-#     y_combobox.addItem("It_net_counts")
-#     y_combobox.setCurrentText("It_net_counts")
-#     r_combobox = display.ui.signal_r_combobox
-#     r_combobox.addItem("I0_net_counts")
-#     r_combobox.setCurrentText("I0_net_counts")
-#     display.ui.signal_r_checkbox.setChecked(True)
-#     display.ui.logarithm_checkbox.setChecked(True)
-#     display.ui.invert_checkbox.setChecked(True)
-#     display.ui.gradient_checkbox.setChecked(True)
-#     # Update the plots
-#     display._db_worker.selected_runs = [run]
-#     display.update_1d_plot()
-#     # Check that the data were added
-#     data_item = display.plot_1d_item.listDataItems()[0]
-#     xdata, ydata = data_item.getData()
-#     np.testing.assert_almost_equal(xdata, expected_xdata)
-#     np.testing.assert_almost_equal(ydata, expected_ydata)
-
-
-# def test_update_multi_plot(client, display, qtbot):
-#     run = client.values()[0]
-#     run_data = run["primary"]["data"].read()
-#     expected_xdata = run_data.energy_energy
-#     expected_ydata = np.log(run_data.I0_net_counts / run_data.It_net_counts)
-#     expected_ydata = np.gradient(expected_ydata, expected_xdata)
-#     with qtbot.waitSignal(display.plot_1d_changed):
-#         display._db_worker.selected_runs_changed.emit([])
-#     # Configure signals
-#     display.ui.multi_signal_x_combobox.addItem("energy_energy")
-#     display.ui.multi_signal_x_combobox.setCurrentText("energy_energy")
-#     display.multi_y_signals = ["energy_energy"]
-#     display._db_worker.selected_runs = [run]
-#     # Update the plots
-#     display.update_multi_plot()
-#     # Check that the data were added
-#     # data_item = display._multiplot_items[0].listDataItems()[0]
-#     # xdata, ydata = data_item.getData()
-#     # np.testing.assert_almost_equal(xdata, expected_xdata)
-#     # np.testing.assert_almost_equal(ydata, expected_ydata)
-
-
-# def test_filter_controls(client, display, qtbot):
-#     # Does editing text change the filters?
-#     display.ui.filter_user_combobox.setCurrentText("")
-#     with qtbot.waitSignal(display.filters_changed):
-#         qtbot.keyClicks(display.ui.filter_user_combobox, "wolfman")
-#     # Set some values for the rest of the controls
-#     display.ui.filter_proposal_combobox.setCurrentText("12345")
-#     display.ui.filter_esaf_combobox.setCurrentText("678901")
-#     display.ui.filter_current_proposal_checkbox.setChecked(True)
-#     display.ui.filter_current_esaf_checkbox.setChecked(True)
-#     display.ui.filter_plan_combobox.addItem("cake")
-#     display.ui.filter_plan_combobox.setCurrentText("cake")
-#     display.ui.filter_full_text_lineedit.setText("Aperature Science")
-#     display.ui.filter_edge_combobox.setCurrentText("U-K")
-#     display.ui.filter_sample_combobox.setCurrentText("Pb.*")
-#     with qtbot.waitSignal(display.filters_changed) as blocker:
-#         display.update_filters()
-#     # Check if the filters were update correctly
-#     filters = blocker.args[0]
-#     assert filters == {
-#         "user": "wolfman",
-#         "proposal": "12345",
-#         "esaf": "678901",
-#         "use_current_proposal": True,
-#         "use_current_esaf": True,
-#         "exit_status": "success",
-#         "plan": "cake",
-#         "full_text": "Aperature Science",
-#         "edge": "U-K",
-#         "sample": "Pb.*",
-#     }
-
-
-# def test_filter_runs(client, qtbot):
-#     worker = DatabaseWorker(root_node=client)
-#     worker._filters["plan"] = "xafs_scan"
-#     with qtbot.waitSignal(worker.all_runs_changed) as blocker:
-#         worker.load_all_runs()
-#     # Check that the runs were filtered
-#     runs = blocker.args[0]
-#     assert len(runs) == 1
-
-
-# def test_distinct_fields(client, qtbot, display):
-#     worker = DatabaseWorker(root_node=client)
-#     with qtbot.waitSignal(worker.distinct_fields_changed) as blocker:
-#         worker.load_distinct_fields()
-#     # Check that the dictionary has the right structure
-#     distinct_fields = blocker.args[0]
-#     for key in ["sample_name"]:
-#         assert key in distinct_fields.keys()
+def test_update_selected_runs(qtbot, display):
+    # Change the proposal item
+    selection_model = display.ui.run_tableview.selectionModel()
+    item = display.runs_model.item(0, 1)
+    assert item is not None
+    rect = display.run_tableview.visualRect(item.index())
+    with qtbot.waitSignal(display._db_worker.selected_runs_changed):
+        qtbot.mouseClick(
+            display.run_tableview.viewport(), Qt.LeftButton, pos=rect.center()
+        )
+    # Check that the runs were saved
+    assert len(display._db_worker.selected_runs) > 0
+
+
+def test_metadata(qtbot, display):
+    # Change the proposal item
+    selection_model = display.ui.run_tableview.selectionModel()
+    item = display.runs_model.item(0, 1)
+    assert item is not None
+    rect = display.run_tableview.visualRect(item.index())
+    with qtbot.waitSignal(display._db_worker.selected_runs_changed):
+        qtbot.mouseClick(
+            display.run_tableview.viewport(), Qt.LeftButton, pos=rect.center()
+        )
+    # Check that the metadata was set properly in the Metadata tab
+    metadata_doc = display.ui.metadata_textedit.document()
+    text = display.ui.metadata_textedit.document().toPlainText()
+    assert "xafs_scan" in text
+
+
+def test_1d_plot_signals(client, display):
+    # Check that the 1D plot was created
+    plot_widget = display.ui.plot_1d_view
+    plot_item = display.plot_1d_item
+    assert isinstance(plot_widget, PlotWidget)
+    assert isinstance(plot_item, PlotItem)
+    # Update the list of runs and see if the controsl get updated
+    display._db_worker.selected_runs = client.values()
+    display._db_worker.selected_runs_changed.emit([])
+    # Check signals in checkboxes
+    for combobox in [
+        display.ui.multi_signal_x_combobox,
+        display.ui.signal_y_combobox,
+        display.ui.signal_r_combobox,
+        display.ui.signal_x_combobox,
+    ]:
+        assert (
+            combobox.findText("energy_energy") > -1
+        ), f"energy_energy signal not in {combobox.objectName()}."
+
+
+def test_1d_plot_signal_memory(client, display):
+    """Do we remember the signals that were previously selected."""
+    # Check that the 1D plot was created
+    plot_widget = display.ui.plot_1d_view
+    plot_item = display.plot_1d_item
+    assert isinstance(plot_widget, PlotWidget)
+    assert isinstance(plot_item, PlotItem)
+    # Update the list of runs and see if the controls get updated
+    display._db_worker.selected_runs = client.values()
+    display.update_1d_signals()
+    # Check signals in comboboxes
+    cb = display.ui.signal_y_combobox
+    assert cb.currentText() == "energy_energy"
+    cb.setCurrentIndex(1)
+    assert cb.currentText() == "energy_id_energy_readback"
+    # Update the combobox signals and make sure the text didn't change
+    display.update_1d_signals()
+    assert cb.currentText() == "energy_id_energy_readback"
+
+
+def test_1d_hinted_signals(client, display):
+    display.ui.plot_1d_hints_checkbox.setChecked(True)
+    # Check that the 1D plot was created
+    plot_widget = display.ui.plot_1d_view
+    plot_item = display.plot_1d_item
+    assert isinstance(plot_widget, PlotWidget)
+    assert isinstance(plot_item, PlotItem)
+    # Update the list of runs and see if the controsl get updated
+    display._db_worker.selected_runs = client.values()
+    display.update_1d_signals()
+    # Check signals in checkboxes
+    combobox = display.ui.signal_x_combobox
+    assert (
+        combobox.findText("energy_energy") > -1
+    ), f"hinted signal not in {combobox.objectName()}."
+    assert (
+        combobox.findText("It_net_counts") == -1
+    ), f"unhinted signal found in {combobox.objectName()}."
+
+
+@pytest.mark.skip(reason="Need to figure out why tiled fails with this test.")
+def test_update_1d_plot(client, display, qtbot):
+    run = client.values()[0]
+    run_data = run["primary"]["data"].read()
+    expected_xdata = run_data.energy_energy
+    expected_ydata = np.log(run_data.I0_net_counts / run_data.It_net_counts)
+    expected_ydata = np.gradient(expected_ydata, expected_xdata)
+    with qtbot.waitSignal(display.plot_1d_changed):
+        display._db_worker.selected_runs_changed.emit([])
+    # Set the controls to describe the data we want to test
+    x_combobox = display.ui.signal_x_combobox
+    x_combobox.addItem("energy_energy")
+    x_combobox.setCurrentText("energy_energy")
+    y_combobox = display.ui.signal_y_combobox
+    y_combobox.addItem("It_net_counts")
+    y_combobox.setCurrentText("It_net_counts")
+    r_combobox = display.ui.signal_r_combobox
+    r_combobox.addItem("I0_net_counts")
+    r_combobox.setCurrentText("I0_net_counts")
+    display.ui.signal_r_checkbox.setChecked(True)
+    display.ui.logarithm_checkbox.setChecked(True)
+    display.ui.invert_checkbox.setChecked(True)
+    display.ui.gradient_checkbox.setChecked(True)
+    # Update the plots
+    display._db_worker.selected_runs = [run]
+    display.update_1d_plot()
+    # Check that the data were added
+    data_item = display.plot_1d_item.listDataItems()[0]
+    xdata, ydata = data_item.getData()
+    np.testing.assert_almost_equal(xdata, expected_xdata)
+    np.testing.assert_almost_equal(ydata, expected_ydata)
+
+
+def test_update_multi_plot(client, display, qtbot):
+    run = client.values()[0]
+    run_data = run["primary"]["data"].read()
+    expected_xdata = run_data.energy_energy
+    expected_ydata = np.log(run_data.I0_net_counts / run_data.It_net_counts)
+    expected_ydata = np.gradient(expected_ydata, expected_xdata)
+    with qtbot.waitSignal(display.plot_1d_changed):
+        display._db_worker.selected_runs_changed.emit([])
+    # Configure signals
+    display.ui.multi_signal_x_combobox.addItem("energy_energy")
+    display.ui.multi_signal_x_combobox.setCurrentText("energy_energy")
+    display.multi_y_signals = ["energy_energy"]
+    display._db_worker.selected_runs = [run]
+    # Update the plots
+    display.update_multi_plot()
+    # Check that the data were added
+    # data_item = display._multiplot_items[0].listDataItems()[0]
+    # xdata, ydata = data_item.getData()
+    # np.testing.assert_almost_equal(xdata, expected_xdata)
+    # np.testing.assert_almost_equal(ydata, expected_ydata)
+
+
+def test_filter_controls(client, display, qtbot):
+    # Does editing text change the filters?
+    display.ui.filter_user_combobox.setCurrentText("")
+    with qtbot.waitSignal(display.filters_changed):
+        qtbot.keyClicks(display.ui.filter_user_combobox, "wolfman")
+    # Set some values for the rest of the controls
+    display.ui.filter_proposal_combobox.setCurrentText("12345")
+    display.ui.filter_esaf_combobox.setCurrentText("678901")
+    display.ui.filter_current_proposal_checkbox.setChecked(True)
+    display.ui.filter_current_esaf_checkbox.setChecked(True)
+    display.ui.filter_plan_combobox.addItem("cake")
+    display.ui.filter_plan_combobox.setCurrentText("cake")
+    display.ui.filter_full_text_lineedit.setText("Aperature Science")
+    display.ui.filter_edge_combobox.setCurrentText("U-K")
+    display.ui.filter_sample_combobox.setCurrentText("Pb.*")
+    with qtbot.waitSignal(display.filters_changed) as blocker:
+        display.update_filters()
+    # Check if the filters were update correctly
+    filters = blocker.args[0]
+    assert filters == {
+        "user": "wolfman",
+        "proposal": "12345",
+        "esaf": "678901",
+        "use_current_proposal": True,
+        "use_current_esaf": True,
+        "exit_status": "success",
+        "plan": "cake",
+        "full_text": "Aperature Science",
+        "edge": "U-K",
+        "sample": "Pb.*",
+    }
+
+
+def test_filter_runs(client, qtbot):
+    worker = DatabaseWorker(root_node=client)
+    worker._filters["plan"] = "xafs_scan"
+    with qtbot.waitSignal(worker.all_runs_changed) as blocker:
+        worker.load_all_runs()
+    # Check that the runs were filtered
+    runs = blocker.args[0]
+    assert len(runs) == 1
+
+
+def test_distinct_fields(client, qtbot, display):
+    worker = DatabaseWorker(root_node=client)
+    with qtbot.waitSignal(worker.distinct_fields_changed) as blocker:
+        worker.load_distinct_fields()
+    # Check that the dictionary has the right structure
+    distinct_fields = blocker.args[0]
+    for key in ["sample_name"]:
+        assert key in distinct_fields.keys()
diff --git a/src/firefly/tests/test_voltmeters.py b/src/firefly/tests/test_voltmeters.py
index f9a89d80..272ef07a 100644
--- a/src/firefly/tests/test_voltmeters.py
+++ b/src/firefly/tests/test_voltmeters.py
@@ -25,68 +25,68 @@ def test_device(qtbot, ffapp, I0):
     assert isinstance(display._device, haven.IonChamber)
 
 
-# def test_scaler_prefix(qtbot, ffapp, sim_registry):
-#     """Make sure the scaler prefix gets passed in as a macro."""
-#     # Set up fake ion chamber
-#     window = FireflyMainWindow()
-#     ic = haven.IonChamber(
-#         "",
-#         scaler_prefix="255idcVME:scaler1",
-#         ch_num=1,
-#         name="my_ion_chamber",
-#         labels={"ion_chambers"},
-#     )
-#     sim_registry.register(ic)
-#     # Check the macros
-#     display = VoltmetersDisplay()
-#     assert display.macros()["SCALER"] == "255idcVME:scaler1"
-
-
-# def test_embedded_display_widgets(qtbot, fake_ion_chambers, ffapp):
-#     """Test the the voltmeters creates a new embedded display widget for
-#     each ion chamber.
-
-#     """
-#     window = FireflyMainWindow()
-#     # Load the display
-#     vms_display = VoltmetersDisplay()
-#     # Check that the embedded display widgets get added correctly
-#     assert hasattr(vms_display, "_ion_chamber_displays")
-#     assert len(vms_display._ion_chamber_displays) == 2
-#     # two displays and a separator
-#     assert vms_display.voltmeters_layout.count() == 3
-#     # Check that the embedded display widgets have the correct macros
-#     emb_disp = vms_display._ion_chamber_displays[0]
-#     disp = emb_disp.open_file(force=True)
-#     macros = disp.macros()
-#     assert macros == {"IC": "I0", "SCALER": "scaler_ioc"}
-#     # Check that a device has been created properly
-#     assert isinstance(disp._device, haven.IonChamber)
-
-
-# def test_ion_chamber_menu(fake_ion_chambers, qtbot, ffapp):
-#     ffapp.setup_window_actions()
-#     ffapp.setup_runengine_actions()
-#     # Create the window
-#     window = FireflyMainWindow()
-#     # Check that the menu items have been created
-#     assert hasattr(window.ui, "menuDetectors")
-#     assert hasattr(window.ui, "menuIonChambers")
-#     assert len(ffapp.ion_chamber_actions) == 2
-
-
-# def test_open_ion_chamber_window(fake_ion_chambers, ffapp):
-#     # Set up the application
-#     ffapp.setup_window_actions()
-#     ffapp.setup_runengine_actions()
-#     # Simulate clicking on the menu action (they're in alpha order)
-#     window = FireflyMainWindow()
-#     action = ffapp.ion_chamber_actions["It"]
-#     action.trigger()
-#     # See if the window was created
-#     ion_chamber_name = "FireflyMainWindow_ion_chamber_It"
-#     assert ion_chamber_name in ffapp.windows.keys()
-#     macros = ffapp.windows[ion_chamber_name].display_widget().macros()
-#     assert macros["IC"] == "It"
-#     # Clean up
-#     window.close()
+def test_scaler_prefix(qtbot, ffapp, sim_registry):
+    """Make sure the scaler prefix gets passed in as a macro."""
+    # Set up fake ion chamber
+    window = FireflyMainWindow()
+    ic = haven.IonChamber(
+        "",
+        scaler_prefix="255idcVME:scaler1",
+        ch_num=1,
+        name="my_ion_chamber",
+        labels={"ion_chambers"},
+    )
+    sim_registry.register(ic)
+    # Check the macros
+    display = VoltmetersDisplay()
+    assert display.macros()["SCALER"] == "255idcVME:scaler1"
+
+
+def test_embedded_display_widgets(qtbot, fake_ion_chambers, ffapp):
+    """Test the the voltmeters creates a new embedded display widget for
+    each ion chamber.
+
+    """
+    window = FireflyMainWindow()
+    # Load the display
+    vms_display = VoltmetersDisplay()
+    # Check that the embedded display widgets get added correctly
+    assert hasattr(vms_display, "_ion_chamber_displays")
+    assert len(vms_display._ion_chamber_displays) == 2
+    # two displays and a separator
+    assert vms_display.voltmeters_layout.count() == 3
+    # Check that the embedded display widgets have the correct macros
+    emb_disp = vms_display._ion_chamber_displays[0]
+    disp = emb_disp.open_file(force=True)
+    macros = disp.macros()
+    assert macros == {"IC": "I0", "SCALER": "scaler_ioc"}
+    # Check that a device has been created properly
+    assert isinstance(disp._device, haven.IonChamber)
+
+
+def test_ion_chamber_menu(fake_ion_chambers, qtbot, ffapp):
+    ffapp.setup_window_actions()
+    ffapp.setup_runengine_actions()
+    # Create the window
+    window = FireflyMainWindow()
+    # Check that the menu items have been created
+    assert hasattr(window.ui, "menuDetectors")
+    assert hasattr(window.ui, "menuIonChambers")
+    assert len(ffapp.ion_chamber_actions) == 2
+
+
+def test_open_ion_chamber_window(fake_ion_chambers, ffapp):
+    # Set up the application
+    ffapp.setup_window_actions()
+    ffapp.setup_runengine_actions()
+    # Simulate clicking on the menu action (they're in alpha order)
+    window = FireflyMainWindow()
+    action = ffapp.ion_chamber_actions["It"]
+    action.trigger()
+    # See if the window was created
+    ion_chamber_name = "FireflyMainWindow_ion_chamber_It"
+    assert ion_chamber_name in ffapp.windows.keys()
+    macros = ffapp.windows[ion_chamber_name].display_widget().macros()
+    assert macros["IC"] == "It"
+    # Clean up
+    window.close()
diff --git a/src/firefly/tests/test_xrf_detector_display.py b/src/firefly/tests/test_xrf_detector_display.py
index e90f1496..531f3ef4 100644
--- a/src/firefly/tests/test_xrf_detector_display.py
+++ b/src/firefly/tests/test_xrf_detector_display.py
@@ -10,7 +10,7 @@
 from firefly.xrf_roi import XRFROIDisplay
 
 
-detectors = ['xspress',]
+detectors = ["dxp", 'xspress']
 
 
 @pytest.fixture()
@@ -91,233 +91,233 @@ def test_all_rois_selection(xrf_display):
     assert not xrf_display.roi_displays[1].isEnabled()
 
 
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_all_mcas_selection(xrf_display):
-#     """Are all the other ROIs disabled when one is selected?"""
-#     mca_display = xrf_display.mca_displays[0]
-#     # Pretend an ROI display was selected
-#     mca_display.selected.emit(True)
-#     # Check that a different ROI display was disabled
-#     assert not xrf_display.mca_displays[1].isEnabled()
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_update_roi_spectra(qtbot, xrf_display):
-#     spectra = np.random.default_rng(seed=0).integers(
-#         0, 65536, dtype=np.int_, size=(4, 1024)
-#     )
-#     roi_plot_widget = xrf_display.ui.roi_plot_widget
-#     with qtbot.waitSignal(roi_plot_widget.plot_changed):
-#         xrf_display._spectrum_channels[0].value_slot(spectra[0])
-#         xrf_display._spectrum_channels[1].value_slot(spectra[1])
-#     # Check that a PlotItem was created
-#     plot_item = roi_plot_widget.ui.plot_widget.getPlotItem()
-#     assert isinstance(plot_item, PlotItem)
-#     # Check that the spectrum was plotted
-#     data_items = plot_item.listDataItems()
-#     assert len(data_items) == 1
-#     # Check that previous plots get cleared
-#     spectra2 = np.random.default_rng(seed=1).integers(
-#         0, 65536, dtype=np.int_, size=(4, 1024)
-#     )
-#     with qtbot.waitSignal(roi_plot_widget.plot_changed):
-#         xrf_display._spectrum_channels[0].value_slot(spectra2[0])
-#     data_items = plot_item.listDataItems()
-#     assert len(data_items) == 1
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_update_mca_spectra(xrf_display, qtbot):
-#     spectra = np.random.default_rng(seed=0).integers(
-#         0, 65536, dtype=np.int_, size=(4, 1024)
-#     )
-#     mca_plot_widget = xrf_display.ui.mca_plot_widget
-#     # Check that a PlotItem was created in the fixture
-#     plot_item = mca_plot_widget.ui.plot_widget.getPlotItem()
-#     assert isinstance(plot_item, PlotItem)
-#     # Clear the data items so we can test them later
-#     plot_item.clear()
-#     # plot_widget.update_spectrum(spectrum=spectra[0], mca_idx=1)
-#     with qtbot.waitSignal(mca_plot_widget.plot_changed):
-#         xrf_display._spectrum_channels[0].value_slot(spectra[0])
-#         xrf_display._spectrum_channels[1].value_slot(spectra[1])
-#     # Check that the spectrum was plotted
-#     data_items = plot_item.listDataItems()
-#     assert len(data_items) == 2
-#     # Check that previous plots get cleared
-#     spectra2 = np.random.default_rng(seed=1).integers(
-#         0, 65536, dtype=np.int_, size=(4, 1024)
-#     )
-#     with qtbot.waitSignal(mca_plot_widget.plot_changed):
-#         xrf_display._spectrum_channels[0].value_slot(spectra2[0])
-#     data_items = plot_item.listDataItems()
-#     assert len(data_items) == 2
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_mca_selected_highlights(qtbot, xrf_display):
-#     """Is the spectrum highlighted when the element row is selected."""
-#     mca_display = xrf_display.mca_displays[1]
-#     spectra = np.random.default_rng(seed=0).integers(
-#         0, 65536, dtype=np.int_, size=(4, 1024)
-#     )
-#     plot_widget = xrf_display.mca_plot_widget
-#     plot_widget.update_spectrum(0, spectra[0])
-#     plot_widget.update_spectrum(1, spectra[1])
-#     this_data_item = xrf_display.mca_plot_widget._data_items[1]
-#     other_data_item = xrf_display.mca_plot_widget._data_items[0]
-#     this_data_item.setOpacity(0.77)
-#     # Select this display and check if the spectrum is highlighted
-#     mca_display.selected.emit(True)
-#     assert this_data_item.opacity() == 1.0
-#     assert other_data_item.opacity() == 0.15
-#     # Hovering other rows should not affect the opacity
-#     xrf_display.mca_displays[0].enterEvent()
-#     assert this_data_item.opacity() == 0.55
-#     assert other_data_item.opacity() == 1.0
-#     xrf_display.mca_displays[0].leaveEvent()
-#     assert this_data_item.opacity() == 1.0
-#     assert other_data_item.opacity() == 0.15
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_show_mca_region_visibility(xrf_display):
-#     """Is the spectrum highlighted when the element row is selected."""
-#     # Check that the region is hidden at startup
-#     plot_widget = xrf_display.mca_plot_widget
-#     region = plot_widget.region(mca_num=2, roi_num=0)
-#     assert not region.isVisible()
-#     # Now highlight a spectrum, and confirm it is visible
-#     plot_widget.highlight_spectrum(mca_num=2, roi_num=0, hovered=True)
-#     assert region.isVisible()
-#     # assert region.brush.color().name() == "#ff7f0e"
-#     # Unhighlight and confirm it is invisible
-#     plot_widget.highlight_spectrum(mca_num=1, roi_num=0, hovered=False)
-#     assert not region.isVisible()
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_show_roi_region(xrf_display):
-#     """Is the spectrum highlighted when the element row is selected."""
-#     # Check that the region is hidden at startup
-#     plot_widget = xrf_display.roi_plot_widget
-#     hovered_region = plot_widget.region(mca_num=1, roi_num=1)
-#     plot_widget.select_roi(mca_num=1, roi_num=0, is_selected=True)
-#     selected_region = plot_widget.region(mca_num=1, roi_num=0)
-#     assert not hovered_region.isVisible()
-#     assert selected_region.isVisible()
-#     # Now highlight a spectrum, and confirm it is visible
-#     plot_widget.highlight_spectrum(mca_num=1, roi_num=1, hovered=True)
-#     assert hovered_region.isVisible()
-#     assert not selected_region.isVisible()
-#     # assert region.brush.color().name() == "#ff7f0e"
-#     # Unhighlight and confirm it is invisible
-#     plot_widget.highlight_spectrum(mca_num=1, roi_num=0, hovered=False)
-#     assert not hovered_region.isVisible()
-#     assert selected_region.isVisible()
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_mca_region_channels(xrf_display):
-#     """Are the channel access connections between the ROI selection region
-#     and the hi/lo channel PVs correct?
-
-#     """
-#     plot_widget = xrf_display.mca_plot_widget
-#     plot_widget.device_name = "vortex_me4"
-#     mca_display = xrf_display.mca_displays[1]
-#     mca_display._embedded_widget = mca_display.open_file(force=True)
-#     xrf_display.mca_selected(is_selected=True, mca_num=2)
-#     correct_address = "sig://vortex_me4.mcas.mca2.rois.roi0.hi_chan"
-#     region = plot_widget.region(mca_num=2, roi_num=0)
-#     assert region.hi_channel.address == correct_address
-#     region.hi_channel.value_slot(108)
-#     assert region.getRegion()[1] == 108
-#     region.lo_channel.value_slot(47)
-#     assert region.getRegion()[0] == 47
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_mca_copyall_button(xrf_display, qtbot):
-#     xrf_display.mca_selected(is_selected=True, mca_num=1)
-#     assert xrf_display.ui.mca_copyall_button.isEnabled()
-#     # Set up ROI displays to test
-#     this_display = xrf_display.mca_displays[1]
-#     this_display._embedded_widget = this_display.open_file(force=True)
-#     other_display = xrf_display.mca_displays[0]
-#     other_display._embedded_widget = other_display.open_file(force=True)
-#     # Change the values on the MCA displays
-#     this_display.embedded_widget.ui.lower_lineedit.setText("111")
-#     this_display.embedded_widget.ui.upper_lineedit.setText("131")
-#     this_display.embedded_widget.ui.label_lineedit.setText("Ni Ka")
-#     # Copy to the other MCA display
-#     qtbot.mouseClick(xrf_display.ui.mca_copyall_button, QtCore.Qt.LeftButton)
-#     assert other_display.embedded_widget.ui.lower_lineedit.text() == "111"
-#     assert other_display.embedded_widget.ui.upper_lineedit.text() == "131"
-#     # Does the button get disabled on un-select?
-#     xrf_display.mca_selected(is_selected=False, mca_num=2)
-#     assert not xrf_display.ui.mca_copyall_button.isEnabled()
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_roi_copyall_button(xrf_display, qtbot):
-#     # Set up ROI rows embedded display widgets
-#     for disp in xrf_display.roi_displays:
-#         disp._embedded_widget = disp.open_file(force=True)
-#     # Select an ROI
-#     xrf_display.roi_selected(is_selected=True, roi_num=1)
-#     assert xrf_display.ui.roi_copyall_button.isEnabled()
-#     # Set up ROI displays to test
-#     this_display = xrf_display.roi_displays[1]
-#     this_display._embedded_widget = this_display.open_file(force=True)
-#     other_display = xrf_display.roi_displays[0]
-#     other_display._embedded_widget = other_display.open_file(force=True)
-#     # Change the values on the MCA displays
-#     this_display.embedded_widget.ui.lower_lineedit.setText("111")
-#     this_display.embedded_widget.ui.upper_lineedit.setText("131")
-#     this_display.embedded_widget.ui.label_lineedit.setText("Ni Ka")
-#     # Copy to the other ROI display
-#     qtbot.mouseClick(xrf_display.ui.roi_copyall_button, QtCore.Qt.LeftButton)
-#     assert other_display.embedded_widget.ui.lower_lineedit.text() == "111"
-#     assert other_display.embedded_widget.ui.upper_lineedit.text() == "131"
-#     # Does the button get disabled on un-select?
-#     xrf_display.roi_selected(is_selected=False, roi_num=1)
-#     assert not xrf_display.ui.roi_copyall_button.isEnabled()
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_mca_enableall_checkbox(xrf_display):
-#     checkbox = xrf_display.ui.mca_enableall_checkbox
-#     assert checkbox.checkState() == QtCore.Qt.PartiallyChecked
-#     assert checkbox.isTristate()
-#     for display in xrf_display.mca_displays:
-#         display._embedded_widget = display.open_file(force=True)
-#         assert not display.embedded_widget.ui.enabled_checkbox.checkState()
-#     # Set it to checked and make sure all the ROI checkboxes respond
-#     checkbox.setCheckState(QtCore.Qt.Checked)
-#     assert not checkbox.isTristate()
-#     for display in xrf_display.mca_displays:
-#         assert display.embedded_widget.ui.enabled_checkbox.isChecked()
-#     # Un-enable all, does it go back?
-#     checkbox.setCheckState(QtCore.Qt.Unchecked)
-#     for display in xrf_display.mca_displays:
-#         assert not display.embedded_widget.ui.enabled_checkbox.isChecked()
-
-
-# @pytest.mark.parametrize("xrf_display", ["dxp", "xspress"], indirect=True)
-# def test_roi_enableall_checkbox(xrf_display):
-#     checkbox = xrf_display.ui.roi_enableall_checkbox
-#     assert checkbox.checkState() == QtCore.Qt.PartiallyChecked
-#     assert checkbox.isTristate()
-#     for display in xrf_display.roi_displays:
-#         display._embedded_widget = display.open_file(force=True)
-#         assert not display.embedded_widget.ui.enabled_checkbox.checkState()
-#     # Set it to checked and make sure all the ROI checkboxes respond
-#     checkbox.setCheckState(QtCore.Qt.Checked)
-#     assert not checkbox.isTristate()
-#     for display in xrf_display.roi_displays:
-#         assert display.embedded_widget.ui.enabled_checkbox.isChecked()
-#     # Un-enable all, does it go back?
-#     checkbox.setCheckState(QtCore.Qt.Unchecked)
-#     for display in xrf_display.roi_displays:
-#         assert not display.embedded_widget.ui.enabled_checkbox.isChecked()
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_all_mcas_selection(xrf_display):
+    """Are all the other ROIs disabled when one is selected?"""
+    mca_display = xrf_display.mca_displays[0]
+    # Pretend an ROI display was selected
+    mca_display.selected.emit(True)
+    # Check that a different ROI display was disabled
+    assert not xrf_display.mca_displays[1].isEnabled()
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_update_roi_spectra(qtbot, xrf_display):
+    spectra = np.random.default_rng(seed=0).integers(
+        0, 65536, dtype=np.int_, size=(4, 1024)
+    )
+    roi_plot_widget = xrf_display.ui.roi_plot_widget
+    with qtbot.waitSignal(roi_plot_widget.plot_changed):
+        xrf_display._spectrum_channels[0].value_slot(spectra[0])
+        xrf_display._spectrum_channels[1].value_slot(spectra[1])
+    # Check that a PlotItem was created
+    plot_item = roi_plot_widget.ui.plot_widget.getPlotItem()
+    assert isinstance(plot_item, PlotItem)
+    # Check that the spectrum was plotted
+    data_items = plot_item.listDataItems()
+    assert len(data_items) == 1
+    # Check that previous plots get cleared
+    spectra2 = np.random.default_rng(seed=1).integers(
+        0, 65536, dtype=np.int_, size=(4, 1024)
+    )
+    with qtbot.waitSignal(roi_plot_widget.plot_changed):
+        xrf_display._spectrum_channels[0].value_slot(spectra2[0])
+    data_items = plot_item.listDataItems()
+    assert len(data_items) == 1
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_update_mca_spectra(xrf_display, qtbot):
+    spectra = np.random.default_rng(seed=0).integers(
+        0, 65536, dtype=np.int_, size=(4, 1024)
+    )
+    mca_plot_widget = xrf_display.ui.mca_plot_widget
+    # Check that a PlotItem was created in the fixture
+    plot_item = mca_plot_widget.ui.plot_widget.getPlotItem()
+    assert isinstance(plot_item, PlotItem)
+    # Clear the data items so we can test them later
+    plot_item.clear()
+    # plot_widget.update_spectrum(spectrum=spectra[0], mca_idx=1)
+    with qtbot.waitSignal(mca_plot_widget.plot_changed):
+        xrf_display._spectrum_channels[0].value_slot(spectra[0])
+        xrf_display._spectrum_channels[1].value_slot(spectra[1])
+    # Check that the spectrum was plotted
+    data_items = plot_item.listDataItems()
+    assert len(data_items) == 2
+    # Check that previous plots get cleared
+    spectra2 = np.random.default_rng(seed=1).integers(
+        0, 65536, dtype=np.int_, size=(4, 1024)
+    )
+    with qtbot.waitSignal(mca_plot_widget.plot_changed):
+        xrf_display._spectrum_channels[0].value_slot(spectra2[0])
+    data_items = plot_item.listDataItems()
+    assert len(data_items) == 2
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_mca_selected_highlights(qtbot, xrf_display):
+    """Is the spectrum highlighted when the element row is selected."""
+    mca_display = xrf_display.mca_displays[1]
+    spectra = np.random.default_rng(seed=0).integers(
+        0, 65536, dtype=np.int_, size=(4, 1024)
+    )
+    plot_widget = xrf_display.mca_plot_widget
+    plot_widget.update_spectrum(0, spectra[0])
+    plot_widget.update_spectrum(1, spectra[1])
+    this_data_item = xrf_display.mca_plot_widget._data_items[1]
+    other_data_item = xrf_display.mca_plot_widget._data_items[0]
+    this_data_item.setOpacity(0.77)
+    # Select this display and check if the spectrum is highlighted
+    mca_display.selected.emit(True)
+    assert this_data_item.opacity() == 1.0
+    assert other_data_item.opacity() == 0.15
+    # Hovering other rows should not affect the opacity
+    xrf_display.mca_displays[0].enterEvent()
+    assert this_data_item.opacity() == 0.55
+    assert other_data_item.opacity() == 1.0
+    xrf_display.mca_displays[0].leaveEvent()
+    assert this_data_item.opacity() == 1.0
+    assert other_data_item.opacity() == 0.15
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_show_mca_region_visibility(xrf_display):
+    """Is the spectrum highlighted when the element row is selected."""
+    # Check that the region is hidden at startup
+    plot_widget = xrf_display.mca_plot_widget
+    region = plot_widget.region(mca_num=2, roi_num=0)
+    assert not region.isVisible()
+    # Now highlight a spectrum, and confirm it is visible
+    plot_widget.highlight_spectrum(mca_num=2, roi_num=0, hovered=True)
+    assert region.isVisible()
+    # assert region.brush.color().name() == "#ff7f0e"
+    # Unhighlight and confirm it is invisible
+    plot_widget.highlight_spectrum(mca_num=1, roi_num=0, hovered=False)
+    assert not region.isVisible()
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_show_roi_region(xrf_display):
+    """Is the spectrum highlighted when the element row is selected."""
+    # Check that the region is hidden at startup
+    plot_widget = xrf_display.roi_plot_widget
+    hovered_region = plot_widget.region(mca_num=1, roi_num=1)
+    plot_widget.select_roi(mca_num=1, roi_num=0, is_selected=True)
+    selected_region = plot_widget.region(mca_num=1, roi_num=0)
+    assert not hovered_region.isVisible()
+    assert selected_region.isVisible()
+    # Now highlight a spectrum, and confirm it is visible
+    plot_widget.highlight_spectrum(mca_num=1, roi_num=1, hovered=True)
+    assert hovered_region.isVisible()
+    assert not selected_region.isVisible()
+    # assert region.brush.color().name() == "#ff7f0e"
+    # Unhighlight and confirm it is invisible
+    plot_widget.highlight_spectrum(mca_num=1, roi_num=0, hovered=False)
+    assert not hovered_region.isVisible()
+    assert selected_region.isVisible()
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_mca_region_channels(xrf_display):
+    """Are the channel access connections between the ROI selection region
+    and the hi/lo channel PVs correct?
+
+    """
+    plot_widget = xrf_display.mca_plot_widget
+    plot_widget.device_name = "vortex_me4"
+    mca_display = xrf_display.mca_displays[1]
+    mca_display._embedded_widget = mca_display.open_file(force=True)
+    xrf_display.mca_selected(is_selected=True, mca_num=2)
+    correct_address = "sig://vortex_me4.mcas.mca2.rois.roi0.hi_chan"
+    region = plot_widget.region(mca_num=2, roi_num=0)
+    assert region.hi_channel.address == correct_address
+    region.hi_channel.value_slot(108)
+    assert region.getRegion()[1] == 108
+    region.lo_channel.value_slot(47)
+    assert region.getRegion()[0] == 47
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_mca_copyall_button(xrf_display, qtbot):
+    xrf_display.mca_selected(is_selected=True, mca_num=1)
+    assert xrf_display.ui.mca_copyall_button.isEnabled()
+    # Set up ROI displays to test
+    this_display = xrf_display.mca_displays[1]
+    this_display._embedded_widget = this_display.open_file(force=True)
+    other_display = xrf_display.mca_displays[0]
+    other_display._embedded_widget = other_display.open_file(force=True)
+    # Change the values on the MCA displays
+    this_display.embedded_widget.ui.lower_lineedit.setText("111")
+    this_display.embedded_widget.ui.upper_lineedit.setText("131")
+    this_display.embedded_widget.ui.label_lineedit.setText("Ni Ka")
+    # Copy to the other MCA display
+    qtbot.mouseClick(xrf_display.ui.mca_copyall_button, QtCore.Qt.LeftButton)
+    assert other_display.embedded_widget.ui.lower_lineedit.text() == "111"
+    assert other_display.embedded_widget.ui.upper_lineedit.text() == "131"
+    # Does the button get disabled on un-select?
+    xrf_display.mca_selected(is_selected=False, mca_num=2)
+    assert not xrf_display.ui.mca_copyall_button.isEnabled()
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_roi_copyall_button(xrf_display, qtbot):
+    # Set up ROI rows embedded display widgets
+    for disp in xrf_display.roi_displays:
+        disp._embedded_widget = disp.open_file(force=True)
+    # Select an ROI
+    xrf_display.roi_selected(is_selected=True, roi_num=1)
+    assert xrf_display.ui.roi_copyall_button.isEnabled()
+    # Set up ROI displays to test
+    this_display = xrf_display.roi_displays[1]
+    this_display._embedded_widget = this_display.open_file(force=True)
+    other_display = xrf_display.roi_displays[0]
+    other_display._embedded_widget = other_display.open_file(force=True)
+    # Change the values on the MCA displays
+    this_display.embedded_widget.ui.lower_lineedit.setText("111")
+    this_display.embedded_widget.ui.upper_lineedit.setText("131")
+    this_display.embedded_widget.ui.label_lineedit.setText("Ni Ka")
+    # Copy to the other ROI display
+    qtbot.mouseClick(xrf_display.ui.roi_copyall_button, QtCore.Qt.LeftButton)
+    assert other_display.embedded_widget.ui.lower_lineedit.text() == "111"
+    assert other_display.embedded_widget.ui.upper_lineedit.text() == "131"
+    # Does the button get disabled on un-select?
+    xrf_display.roi_selected(is_selected=False, roi_num=1)
+    assert not xrf_display.ui.roi_copyall_button.isEnabled()
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_mca_enableall_checkbox(xrf_display):
+    checkbox = xrf_display.ui.mca_enableall_checkbox
+    assert checkbox.checkState() == QtCore.Qt.PartiallyChecked
+    assert checkbox.isTristate()
+    for display in xrf_display.mca_displays:
+        display._embedded_widget = display.open_file(force=True)
+        assert not display.embedded_widget.ui.enabled_checkbox.checkState()
+    # Set it to checked and make sure all the ROI checkboxes respond
+    checkbox.setCheckState(QtCore.Qt.Checked)
+    assert not checkbox.isTristate()
+    for display in xrf_display.mca_displays:
+        assert display.embedded_widget.ui.enabled_checkbox.isChecked()
+    # Un-enable all, does it go back?
+    checkbox.setCheckState(QtCore.Qt.Unchecked)
+    for display in xrf_display.mca_displays:
+        assert not display.embedded_widget.ui.enabled_checkbox.isChecked()
+
+
+@pytest.mark.parametrize("xrf_display", detectors, indirect=True)
+def test_roi_enableall_checkbox(xrf_display):
+    checkbox = xrf_display.ui.roi_enableall_checkbox
+    assert checkbox.checkState() == QtCore.Qt.PartiallyChecked
+    assert checkbox.isTristate()
+    for display in xrf_display.roi_displays:
+        display._embedded_widget = display.open_file(force=True)
+        assert not display.embedded_widget.ui.enabled_checkbox.checkState()
+    # Set it to checked and make sure all the ROI checkboxes respond
+    checkbox.setCheckState(QtCore.Qt.Checked)
+    assert not checkbox.isTristate()
+    for display in xrf_display.roi_displays:
+        assert display.embedded_widget.ui.enabled_checkbox.isChecked()
+    # Un-enable all, does it go back?
+    checkbox.setCheckState(QtCore.Qt.Unchecked)
+    for display in xrf_display.roi_displays:
+        assert not display.embedded_widget.ui.enabled_checkbox.isChecked()
diff --git a/src/haven/instrument/xspress.py b/src/haven/instrument/xspress.py
index dc63e9b4..3dea0ca2 100644
--- a/src/haven/instrument/xspress.py
+++ b/src/haven/instrument/xspress.py
@@ -81,30 +81,30 @@
 class ChannelSignal(MultiDerivedSignal):
     """A high/low range limit channel for an ROI."""
 
-    # def set(
-    #     self,
-    #     value: OphydDataType,
-    #     *,
-    #     timeout: Optional[float] = None,
-    #     settle_time: Optional[float] = None,
-    # ) -> StatusBase:
-    #     # Check for existing signals and, if necessary, wait them out
-    #     signals = [
-    #         self.parent.hi_chan,
-    #         self.parent.lo_chan,
-    #         self.parent.size,
-    #         self.parent._lo_chan,
-    #     ]
-
-    #     def get_threads():
-    #         thds = [sig._set_thread for sig in signals if sig._set_thread]
-    #         return [th for th in thds if th is not None]
-
-    #     while len(threads := get_threads()) > 0:
-    #         for th in threads:
-    #             th.join()
-    #     # Set the signal like normal
-    #     return super().set(value, timeout=timeout, settle_time=settle_time)
+    def set(
+        self,
+        value: OphydDataType,
+        *,
+        timeout: Optional[float] = None,
+        settle_time: Optional[float] = None,
+    ) -> StatusBase:
+        # Check for existing signals and, if necessary, wait them out
+        signals = [
+            self.parent.hi_chan,
+            self.parent.lo_chan,
+            self.parent.size,
+            self.parent._lo_chan,
+        ]
+
+        def get_threads():
+            thds = [sig._set_thread for sig in signals if sig._set_thread]
+            return [th for th in thds if th is not None]
+
+        while len(threads := get_threads()) > 0:
+            for th in threads:
+                th.join()
+        # Set the signal like normal
+        return super().set(value, timeout=timeout, settle_time=settle_time)
 
 
 class ROI(ROIMixin):

From 6e2350b6f6f53aaf642e38ea6565b22deff170cc Mon Sep 17 00:00:00 2001
From: Mark Wolfman <canismarko@gmail.com>
Date: Wed, 15 Nov 2023 21:59:38 -0600
Subject: [PATCH 6/9] Black and isort.

---
 .github/workflows/ci.yml                      |  8 +-
 environment.yml                               |  2 +-
 pyproject.toml                                |  3 +
 src/conftest.py                               | 33 +++++----
 src/firefly/__init__.py                       |  1 -
 src/firefly/application.py                    | 33 +++++----
 src/firefly/area_detector_viewer.py           |  9 +--
 src/firefly/bss.py                            |  4 +-
 src/firefly/button.py                         |  2 +-
 src/firefly/camera.py                         | 14 ++--
 src/firefly/cameras.py                        |  3 +-
 src/firefly/detector_list.py                  |  2 +-
 src/firefly/display.py                        |  6 +-
 src/firefly/energy.py                         |  7 +-
 src/firefly/ion_chamber.py                    |  1 -
 src/firefly/launcher.py                       | 13 ++--
 src/firefly/main_window.py                    |  8 +-
 src/firefly/plans/count.py                    |  2 +-
 src/firefly/queue_button.py                   |  8 +-
 src/firefly/queue_client.py                   | 20 ++---
 src/firefly/run_browser.py                    | 23 +++---
 src/firefly/run_client.py                     |  3 +-
 src/firefly/status.py                         |  3 +-
 src/firefly/tests/test_application.py         | 11 +--
 .../tests/test_area_detector_display.py       |  5 +-
 src/firefly/tests/test_bss_display.py         | 52 +++++++------
 src/firefly/tests/test_cameras_display.py     |  9 +--
 src/firefly/tests/test_count_window.py        |  2 +-
 src/firefly/tests/test_energy_display.py      | 29 +++-----
 src/firefly/tests/test_main_window.py         |  4 +-
 src/firefly/tests/test_motor_menu.py          | 12 +--
 src/firefly/tests/test_ophyd_connection.py    | 10 +--
 src/firefly/tests/test_queue_client.py        | 30 +++++---
 src/firefly/tests/test_run_browser.py         | 20 ++---
 src/firefly/tests/test_tiled_server.py        |  8 +-
 src/firefly/tests/test_voltmeters.py          |  5 +-
 .../tests/test_xrf_detector_display.py        |  7 +-
 src/firefly/voltmeter.py                      | 10 +--
 src/firefly/voltmeters.py                     |  8 +-
 src/firefly/xrf_detector.py                   | 42 +++++------
 src/haven/__init__.py                         | 69 +++++++++---------
 src/haven/_iconfig.py                         | 10 +--
 src/haven/energy_ranges.py                    |  1 -
 src/haven/exceptions.py                       |  2 +-
 src/haven/instrument/__init__.py              |  4 +-
 src/haven/instrument/aerotech.py              | 50 ++++++-------
 src/haven/instrument/aps.py                   |  6 +-
 src/haven/instrument/area_detector.py         | 34 ++++-----
 src/haven/instrument/camera.py                | 18 ++---
 src/haven/instrument/delay.py                 |  3 +-
 src/haven/instrument/device.py                | 11 +--
 src/haven/instrument/dxp.py                   | 57 ++++++---------
 src/haven/instrument/energy_positioner.py     | 19 ++---
 src/haven/instrument/fluorescence_detector.py | 51 +++++--------
 src/haven/instrument/heater.py                | 13 ++--
 src/haven/instrument/instrument_registry.py   |  5 +-
 src/haven/instrument/ion_chamber.py           | 59 ++++++++-------
 src/haven/instrument/labjack.py               | 13 +---
 src/haven/instrument/lerix.py                 | 10 ++-
 src/haven/instrument/load_instrument.py       | 26 +++----
 src/haven/instrument/monochromator.py         | 18 ++---
 src/haven/instrument/motor.py                 | 18 ++---
 src/haven/instrument/power_supply.py          | 16 ++--
 src/haven/instrument/scaler_triggered.py      |  9 +--
 src/haven/instrument/shutter.py               | 11 +--
 src/haven/instrument/slits.py                 |  5 +-
 src/haven/instrument/stage.py                 | 39 ++++------
 src/haven/instrument/xray_source.py           | 10 +--
 src/haven/instrument/xspress.py               | 67 ++++++++---------
 src/haven/motor_position.py                   | 15 ++--
 src/haven/plans/align_motor.py                |  8 +-
 src/haven/plans/align_slits.py                |  8 +-
 src/haven/plans/auto_gain.py                  |  3 +-
 src/haven/plans/beam_properties.py            |  2 +-
 src/haven/plans/energy_scan.py                |  9 +--
 src/haven/plans/fly.py                        |  8 +-
 src/haven/plans/mono_ID_calibration.py        | 12 +--
 src/haven/plans/mono_gap_calibration.py       | 15 ++--
 src/haven/plans/record_dark_current.py        |  5 +-
 src/haven/plans/set_energy.py                 |  5 +-
 src/haven/plans/shutters.py                   |  3 +-
 src/haven/plans/xafs_scan.py                  | 12 +--
 src/haven/preprocessors.py                    | 28 +++----
 src/haven/run_engine.py                       |  8 +-
 src/haven/simulated_ioc.py                    | 24 ++----
 src/haven/tests/ioc_apsbss.py                 |  4 +-
 src/haven/tests/ioc_area_detector.py          |  3 +-
 src/haven/tests/ioc_dxp.py                    |  8 +-
 src/haven/tests/ioc_preamp.py                 |  4 +-
 src/haven/tests/ioc_ptc10.py                  |  1 -
 src/haven/tests/ioc_scaler.py                 |  1 -
 src/haven/tests/ioc_simple.py                 |  2 +-
 src/haven/tests/ioc_undulator.py              |  2 +-
 src/haven/tests/test_aerotech.py              |  9 ++-
 src/haven/tests/test_align_motor.py           |  4 +-
 src/haven/tests/test_beam_properties.py       |  6 +-
 src/haven/tests/test_camera.py                |  3 +-
 src/haven/tests/test_device.py                |  4 +-
 src/haven/tests/test_energy_positioner.py     |  2 +-
 src/haven/tests/test_energy_ranges.py         |  3 +-
 src/haven/tests/test_energy_xafs_scan.py      |  8 +-
 .../tests/test_fluorescence_detectors.py      | 12 +--
 src/haven/tests/test_fly_plans.py             |  8 +-
 src/haven/tests/test_heater.py                |  2 +-
 src/haven/tests/test_iconfig.py               |  4 +-
 src/haven/tests/test_instrument_registry.py   |  3 +-
 src/haven/tests/test_ion_chamber.py           |  6 +-
 src/haven/tests/test_lerix.py                 |  7 +-
 .../tests/test_mono_ID_calibration_plan.py    |  7 +-
 src/haven/tests/test_plans.py                 |  4 +-
 src/haven/tests/test_preprocessors.py         | 10 +--
 src/haven/tests/test_run_engine.py            |  2 +-
 src/haven/tests/test_save_motor_positions.py  | 22 +++---
 src/haven/tests/test_scaler_triggering.py     |  1 -
 src/haven/tests/test_set_energy.py            |  2 +-
 src/haven/tests/test_shutter.py               |  3 +-
 src/haven/tests/test_stages.py                |  9 ++-
 src/haven/tests/test_xdi_writer.py            | 73 +++++++++++--------
 src/haven/tests/test_xray_source.py           |  2 +-
 src/haven/tests/test_xspress.py               |  3 +-
 src/haven/typing.py                           |  5 +-
 src/haven/xdi_writer.py                       |  9 +--
 122 files changed, 740 insertions(+), 809 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aa30fb63..813f7ad3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -36,10 +36,14 @@ jobs:
         env
         micromamba info
         micromamba list
-    - name: Lint with flake8
+    - name: Lint
       run: |
-        # stop the build if there are Python syntax errors or undefined names
+        # Check for syntax errors or undefined names
         flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
+        # Make sure black code formatting is applied
+        black --check --preview src/
+        # Make sure import orders are correct
+        isort --check src/
         # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
     - name: Test with pytest in Xvfb
diff --git a/environment.yml b/environment.yml
index bb35fa30..4896ec6b 100644
--- a/environment.yml
+++ b/environment.yml
@@ -34,7 +34,7 @@ dependencies:
   - numba>=0.56  # Ensures 0.53 (broken) isn't installed
 
   # --- testing and quality assurance
-  - black
+  - black >=22.1
   - flake8
   - pre-commit
   - pylint
diff --git a/pyproject.toml b/pyproject.toml
index 08353756..109f4ba8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -37,3 +37,6 @@ firefly_cameras = "firefly.launcher:cameras"
 [build-system]
 requires = ["setuptools>=61.0"]
 build-backend = "setuptools.build_meta"
+
+[tool.isort]
+profile = "black"
diff --git a/src/conftest.py b/src/conftest.py
index 81b81577..ef3bd3c8 100644
--- a/src/conftest.py
+++ b/src/conftest.py
@@ -1,35 +1,38 @@
-from unittest import mock
+import os
 import subprocess
-import psutil
 from pathlib import Path
-import os
+from unittest import mock
 
-from bluesky import RunEngine
+import psutil
+
+# from pydm.data_plugins import plugin_modules, add_plugin
+import pydm
 import pytest
-from ophyd import DynamicDeviceComponent as DDC, Kind
+from bluesky import RunEngine
+from ophyd import DynamicDeviceComponent as DDC
+from ophyd import Kind
 from ophyd.sim import (
+    FakeEpicsSignal,
+    fake_device_cache,
     instantiate_fake_device,
     make_fake_device,
-    fake_device_cache,
-    FakeEpicsSignal,
 )
-# from pydm.data_plugins import plugin_modules, add_plugin
-import pydm
 from pytestqt.qt_compat import qt_api
 
 import haven
+from firefly.application import FireflyApplication
+from firefly.main_window import FireflyMainWindow
 from haven._iconfig import beamline_connected as _beamline_connected
 from haven.instrument.aerotech import AerotechStage
 from haven.instrument.aps import ApsMachine
-from haven.instrument.shutter import Shutter
 from haven.instrument.camera import AravisDetector
 from haven.instrument.delay import EpicsSignalWithIO
-from haven.instrument.dxp import DxpDetector, add_mcas as add_dxp_mcas
+from haven.instrument.dxp import DxpDetector
+from haven.instrument.dxp import add_mcas as add_dxp_mcas
 from haven.instrument.ion_chamber import IonChamber
-from haven.instrument.xspress import Xspress3Detector, add_mcas as add_xspress_mcas
-from firefly.application import FireflyApplication
-from firefly.main_window import FireflyMainWindow
-
+from haven.instrument.shutter import Shutter
+from haven.instrument.xspress import Xspress3Detector
+from haven.instrument.xspress import add_mcas as add_xspress_mcas
 
 top_dir = Path(__file__).parent.resolve()
 haven_dir = top_dir / "haven"
diff --git a/src/firefly/__init__.py b/src/firefly/__init__.py
index 288b43d1..ed977ab9 100644
--- a/src/firefly/__init__.py
+++ b/src/firefly/__init__.py
@@ -1,3 +1,2 @@
 from . import display
-
 from .application import FireflyApplication
diff --git a/src/firefly/application.py b/src/firefly/application.py
index a8846cac..5cea055c 100644
--- a/src/firefly/application.py
+++ b/src/firefly/application.py
@@ -1,27 +1,28 @@
 import logging
+import subprocess
 from collections import OrderedDict
-from pathlib import Path
 from dataclasses import dataclass, field
-from typing import Optional, Union, Mapping, Sequence
 from functools import partial
-import subprocess
+from pathlib import Path
+from typing import Mapping, Optional, Sequence, Union
 
-from qtpy import QtWidgets, QtCore
-from qtpy.QtWidgets import QAction
-from qtpy.QtCore import Slot, QThread, Signal, QObject
-from PyQt5.QtWidgets import QStyleFactory
-import qtawesome as qta
 import pydm
+import pyqtgraph as pg
+import qtawesome as qta
+from bluesky_queueserver_api import BPlan
+from bluesky_queueserver_api.zmq import REManagerAPI
 from pydm.application import PyDMApplication
 from pydm.display import load_file
 from pydm.utilities.stylesheet import apply_stylesheet
-from bluesky_queueserver_api import BPlan
-from bluesky_queueserver_api.zmq import REManagerAPI
-import pyqtgraph as pg
+from PyQt5.QtWidgets import QStyleFactory
+from qtpy import QtCore, QtWidgets
+from qtpy.QtCore import QObject, QThread, Signal, Slot
+from qtpy.QtWidgets import QAction
 
-from haven.exceptions import ComponentNotFound
-from haven import HavenMotor, registry, load_config
 import haven
+from haven import HavenMotor, load_config, registry
+from haven.exceptions import ComponentNotFound
+
 from .main_window import FireflyMainWindow, PlanMainWindow
 from .queue_client import QueueClient, QueueClientThread, queueserver_api
 
@@ -339,7 +340,11 @@ def prepare_queue_client(self, api=None):
             thread = QueueClientThread()
             self._queue_thread = thread
         # Create the client object
-        client = QueueClient(api=api, autoplay_action=self.queue_autoplay_action, open_environment_action=self.queue_open_environment_action)
+        client = QueueClient(
+            api=api,
+            autoplay_action=self.queue_autoplay_action,
+            open_environment_action=self.queue_open_environment_action,
+        )
         client.moveToThread(thread)
         thread.timer.timeout.connect(client.update)
         self._queue_client = client
diff --git a/src/firefly/area_detector_viewer.py b/src/firefly/area_detector_viewer.py
index f0400be3..c180cf51 100644
--- a/src/firefly/area_detector_viewer.py
+++ b/src/firefly/area_detector_viewer.py
@@ -1,16 +1,15 @@
 import logging
 import subprocess
+import sys
 
-import pyqtgraph
-import pydm
-import numpy as np
 import matplotlib.pyplot as plt
+import numpy as np
+import pydm
+import pyqtgraph
 
 import haven
 from firefly import display
 
-import sys
-
 np.set_printoptions(threshold=sys.maxsize)
 
 
diff --git a/src/firefly/bss.py b/src/firefly/bss.py
index ebcd5d0a..2bab7ca0 100644
--- a/src/firefly/bss.py
+++ b/src/firefly/bss.py
@@ -1,10 +1,10 @@
 import logging
 from functools import lru_cache
 
+import qtawesome as qta
 from apsbss import apsbss
-from qtpy.QtGui import QStandardItemModel, QStandardItem
 from qtpy.QtCore import Signal, Slot
-import qtawesome as qta
+from qtpy.QtGui import QStandardItem, QStandardItemModel
 
 import haven
 from firefly import display
diff --git a/src/firefly/button.py b/src/firefly/button.py
index 5e6bafdf..398e9b99 100644
--- a/src/firefly/button.py
+++ b/src/firefly/button.py
@@ -1,6 +1,6 @@
+import qtawesome as qta
 from qtpy import QtWidgets
 from qtpy.QtGui import QIcon
-import qtawesome as qta
 
 
 class RevealButton(QtWidgets.QPushButton):
diff --git a/src/firefly/camera.py b/src/firefly/camera.py
index 0bec98c6..a5a0330a 100644
--- a/src/firefly/camera.py
+++ b/src/firefly/camera.py
@@ -1,19 +1,17 @@
-from enum import IntEnum
-from pathlib import Path
 import datetime as dt
-import subprocess
 import logging
 import os
-
-import haven
+import subprocess
+from enum import IntEnum
+from pathlib import Path
 
 # from pydm.data_plugins.epics_plugin import EPICSPlugin
 from pydm.widgets.channel import PyDMChannel
-from qtpy.QtGui import QColor
 from qtpy.QtCore import Slot
+from qtpy.QtGui import QColor
 
-from firefly import display, FireflyApplication
-
+import haven
+from firefly import FireflyApplication, display
 
 log = logging.getLogger(__name__)
 
diff --git a/src/firefly/cameras.py b/src/firefly/cameras.py
index 1491200d..58f93f55 100644
--- a/src/firefly/cameras.py
+++ b/src/firefly/cameras.py
@@ -2,11 +2,10 @@
 import logging
 
 from pydm.widgets import PyDMEmbeddedDisplay
-import haven
 
+import haven
 from firefly import display
 
-
 log = logging.getLogger(__name__)
 
 
diff --git a/src/firefly/detector_list.py b/src/firefly/detector_list.py
index 8d58fa95..8557c68b 100644
--- a/src/firefly/detector_list.py
+++ b/src/firefly/detector_list.py
@@ -1,4 +1,4 @@
-from PyQt5.QtGui import QStandardItemModel, QStandardItem
+from PyQt5.QtGui import QStandardItem, QStandardItemModel
 from qtpy.QtWidgets import QListView
 
 from haven import registry
diff --git a/src/firefly/display.py b/src/firefly/display.py
index 183e4e2c..6cc60671 100644
--- a/src/firefly/display.py
+++ b/src/firefly/display.py
@@ -1,10 +1,10 @@
-from typing import Sequence
 import subprocess
 from pathlib import Path
+from typing import Sequence
 
-from qtpy.QtCore import Signal, Slot
-from qtpy import QtWidgets
 from pydm import Display
+from qtpy import QtWidgets
+from qtpy.QtCore import Signal, Slot
 
 
 class FireflyDisplay(Display):
diff --git a/src/firefly/energy.py b/src/firefly/energy.py
index 7f15620c..440bfdb4 100644
--- a/src/firefly/energy.py
+++ b/src/firefly/energy.py
@@ -1,13 +1,12 @@
 import logging
 
-from qtpy import QtWidgets, QtCore
+import qtawesome as qta
 from bluesky_queueserver_api import BPlan
-from haven import registry, load_config, exceptions
+from qtpy import QtCore, QtWidgets
 from xraydb.xraydb import XrayDB
-import qtawesome as qta
 
 from firefly import display
-
+from haven import exceptions, load_config, registry
 
 log = logging.getLogger(__name__)
 
diff --git a/src/firefly/ion_chamber.py b/src/firefly/ion_chamber.py
index 4c5b9756..99707dca 100644
--- a/src/firefly/ion_chamber.py
+++ b/src/firefly/ion_chamber.py
@@ -5,6 +5,5 @@
 
 
 class IonChamberDisplay(display.FireflyDisplay):
-
     def ui_filename(self):
         return "ion_chamber.ui"
diff --git a/src/firefly/launcher.py b/src/firefly/launcher.py
index dd8b36fd..faaa91e2 100644
--- a/src/firefly/launcher.py
+++ b/src/firefly/launcher.py
@@ -1,12 +1,11 @@
-import time
 import argparse
 import cProfile
 import logging
 import pstats
 import sys
+import time
 from pathlib import Path
 
-
 import haven
 
 
@@ -29,10 +28,11 @@ def main(default_fullscreen=False, default_display="status"):
     except ImportError:
         logger.debug("QtWebEngine is not supported.")
 
+    from qtpy import QtCore
+    from qtpy.QtGui import QPixmap
     from qtpy.QtWidgets import QSplashScreen
+
     from .application import FireflyApplication
-    from qtpy.QtGui import QPixmap
-    from qtpy import QtCore
 
     # Set up splash screen
     fake_app = FireflyApplication(sys.argv)
@@ -70,7 +70,10 @@ def main(default_fullscreen=False, default_display="status"):
     parser.add_argument(
         "--no-instrument",
         action="store_true",
-        help="Do not try to create devices. Useful for development if much beamline hardware is offline.",
+        help=(
+            "Do not try to create devices. Useful for development if much beamline"
+            " hardware is offline."
+        ),
     )
     parser.add_argument(
         "--perfmon",
diff --git a/src/firefly/main_window.py b/src/firefly/main_window.py
index a2b0409f..a39299b1 100644
--- a/src/firefly/main_window.py
+++ b/src/firefly/main_window.py
@@ -1,15 +1,15 @@
 import logging
-from pathlib import Path
 import warnings
+from pathlib import Path
 
+from pydm import data_plugins
 from pydm.main_window import PyDMMainWindow
 
 # from qtpy.QtCore import Slot
 from qtpy import QtCore, QtGui, QtWidgets
-from pydm import data_plugins
-from haven.instrument import motor
-from haven.instrument import motor
+
 from haven import load_config
+from haven.instrument import motor
 
 log = logging.getLogger(__name__)
 
diff --git a/src/firefly/plans/count.py b/src/firefly/plans/count.py
index bd4fa917..77373ab2 100644
--- a/src/firefly/plans/count.py
+++ b/src/firefly/plans/count.py
@@ -1,7 +1,7 @@
 import logging
 
-from qtpy import QtWidgets
 from bluesky_queueserver_api import BPlan
+from qtpy import QtWidgets
 
 from firefly import display
 
diff --git a/src/firefly/queue_button.py b/src/firefly/queue_button.py
index d287464a..0441f23d 100644
--- a/src/firefly/queue_button.py
+++ b/src/firefly/queue_button.py
@@ -1,7 +1,7 @@
 """A QPushButton that responds to the state of the queue server."""
 
-from qtpy import QtWidgets, QtGui
 import qtawesome as qta
+from qtpy import QtGui, QtWidgets
 
 from firefly import FireflyApplication
 
@@ -26,8 +26,7 @@ def handle_queue_status_change(self, status: dict):
         if status["re_state"] == "idle" and app.queue_autoplay_action.isChecked():
             # Will play immediately
             self.setStyleSheet(
-                "background-color: rgb(25, 135, 84);\n"
-                "border-color: rgb(25, 135, 84);"
+                "background-color: rgb(25, 135, 84);\nborder-color: rgb(25, 135, 84);"
             )
             self.setIcon(qta.icon("fa5s.play"))
             self.setText("Run")
@@ -35,8 +34,7 @@ def handle_queue_status_change(self, status: dict):
         elif status["worker_environment_exists"]:
             # Will be added to the queue
             self.setStyleSheet(
-                "background-color: rgb(0, 123, 255);\n"
-                "border-color: rgb(0, 123, 255);"
+                "background-color: rgb(0, 123, 255);\nborder-color: rgb(0, 123, 255);"
             )
             self.setIcon(qta.icon("fa5s.list"))
             self.setText("Add to Queue")
diff --git a/src/firefly/queue_client.py b/src/firefly/queue_client.py
index d98bf935..6df5f5e7 100644
--- a/src/firefly/queue_client.py
+++ b/src/firefly/queue_client.py
@@ -1,16 +1,15 @@
-import time
-from typing import Optional
 import logging
+import time
 import warnings
+from typing import Optional
 
-from qtpy.QtWidgets import QAction
-from qtpy.QtCore import QThread, QObject, Signal, Slot, QTimer
-from bluesky_queueserver_api.zmq import REManagerAPI
 from bluesky_queueserver_api import BPlan, comm_base
+from bluesky_queueserver_api.zmq import REManagerAPI
+from qtpy.QtCore import QObject, QThread, QTimer, Signal, Slot
+from qtpy.QtWidgets import QAction
 
 from haven import load_config
 
-
 log = logging.getLogger()
 
 
@@ -30,6 +29,7 @@ class QueueClientThread(QThread):
     :py:cls:`QueueClientThread.timer.timeout()` signal.
 
     """
+
     timer: QTimer
 
     def __init__(self, *args, poll_time=1000, **kwargs):
@@ -217,9 +217,8 @@ def _check_queue_status(self, force: bool = False):
             if is_new or has_changed or force:
                 signal.emit(new_status[key])
         # Check for new available devices
-        if (
-            new_status["devices_allowed_uid"]
-            != self._last_queue_status.get("devices_allowed_uid")
+        if new_status["devices_allowed_uid"] != self._last_queue_status.get(
+            "devices_allowed_uid"
         ):
             self.update_devices()
         # check the whole status to see if it's changed
@@ -236,5 +235,6 @@ def update_devices(self):
             self.devices_changed.emit(devices)
         else:
             log.warning(
-                f"Could not poll devices_allowed: {response.get('msg', 'reason unknown.')}"
+                "Could not poll devices_allowed:"
+                f" {response.get('msg', 'reason unknown.')}"
             )
diff --git a/src/firefly/run_browser.py b/src/firefly/run_browser.py
index 59bd0765..4f112a42 100644
--- a/src/firefly/run_browser.py
+++ b/src/firefly/run_browser.py
@@ -1,24 +1,24 @@
-import logging
 import datetime as dt
-from typing import Sequence
+import logging
 import warnings
-import yaml
-from httpx import HTTPStatusError, PoolTimeout
 from contextlib import contextmanager
 from itertools import count
+from typing import Sequence
 
 import numpy as np
-from qtpy.QtWidgets import QWidget
-from qtpy.QtGui import QStandardItemModel, QStandardItem
-from qtpy.QtCore import Signal, Slot, QThread, Qt
-from pyqtgraph import PlotItem, GraphicsLayoutWidget, PlotWidget, PlotDataItem
 import qtawesome as qta
+import yaml
+from httpx import HTTPStatusError, PoolTimeout
 from matplotlib.colors import TABLEAU_COLORS
 from pydantic.error_wrappers import ValidationError
+from pyqtgraph import GraphicsLayoutWidget, PlotDataItem, PlotItem, PlotWidget
+from qtpy.QtCore import Qt, QThread, Signal, Slot
+from qtpy.QtGui import QStandardItem, QStandardItemModel
+from qtpy.QtWidgets import QWidget
 
-from firefly import display, FireflyApplication
+from firefly import FireflyApplication, display
 from firefly.run_client import DatabaseWorker
-from haven import tiled_client, load_config, exceptions
+from haven import exceptions, load_config, tiled_client
 
 log = logging.getLogger(__name__)
 
@@ -289,7 +289,8 @@ def calculate_ydata(
     def load_run_data(self, run, x_signal, y_signal, r_signal, use_reference=True):
         if "" in [x_signal, y_signal] or (use_reference and r_signal == ""):
             log.debug(
-                f"Empty signal name requested: x='{x_signal}', y='{y_signal}', r='{r_signal}'"
+                f"Empty signal name requested: x='{x_signal}', y='{y_signal}',"
+                f" r='{r_signal}'"
             )
             raise exceptions.EmptySignalName
         signals = [x_signal, y_signal]
diff --git a/src/firefly/run_client.py b/src/firefly/run_client.py
index 95cf65dd..227c49fd 100644
--- a/src/firefly/run_client.py
+++ b/src/firefly/run_client.py
@@ -3,12 +3,11 @@
 from collections import OrderedDict
 from typing import Sequence
 
-from qtpy.QtCore import QObject, Slot, Signal
+from qtpy.QtCore import QObject, Signal, Slot
 from tiled import queries
 
 from haven import tiled_client
 
-
 log = logging.getLogger(__name__)
 
 
diff --git a/src/firefly/status.py b/src/firefly/status.py
index 556f9aaf..2e830b3f 100644
--- a/src/firefly/status.py
+++ b/src/firefly/status.py
@@ -1,8 +1,7 @@
 import logging
 
 import haven
-
-from firefly import display, FireflyApplication
+from firefly import FireflyApplication, display
 
 log = logging.getLogger(__name__)
 
diff --git a/src/firefly/tests/test_application.py b/src/firefly/tests/test_application.py
index a9411692..5f10cefa 100644
--- a/src/firefly/tests/test_application.py
+++ b/src/firefly/tests/test_application.py
@@ -1,15 +1,16 @@
+import asyncio
 import time
-import pytest
 from unittest.mock import MagicMock
-import asyncio
 
-from bluesky import RunEngine, plans as bp
+import pytest
+from bluesky import RunEngine
+from bluesky import plans as bp
+from bluesky_queueserver_api.zmq import REManagerAPI
 from qtpy.QtCore import QThread
 from qtpy.QtTest import QSignalSpy
-from bluesky_queueserver_api.zmq import REManagerAPI
 
-from firefly.queue_client import QueueClient
 from firefly.application import REManagerAPI
+from firefly.queue_client import QueueClient
 
 
 def test_setup(ffapp):
diff --git a/src/firefly/tests/test_area_detector_display.py b/src/firefly/tests/test_area_detector_display.py
index 50d9c8e8..c630bfd2 100644
--- a/src/firefly/tests/test_area_detector_display.py
+++ b/src/firefly/tests/test_area_detector_display.py
@@ -1,7 +1,8 @@
-import pyqtgraph
+from unittest import mock
+
 import numpy as np
 import pydm
-from unittest import mock
+import pyqtgraph
 
 from firefly.area_detector_viewer import AreaDetectorViewerDisplay
 from haven.instrument.camera import load_cameras
diff --git a/src/firefly/tests/test_bss_display.py b/src/firefly/tests/test_bss_display.py
index c0ae6a4e..a78ee358 100644
--- a/src/firefly/tests/test_bss_display.py
+++ b/src/firefly/tests/test_bss_display.py
@@ -1,11 +1,12 @@
-import pytest
 import time
-from qtpy.QtGui import QStandardItemModel
-from qtpy.QtCore import Qt
+
+import pytest
 from epics import caget, caput
+from qtpy.QtCore import Qt
+from qtpy.QtGui import QStandardItemModel
 
-from haven.instrument.aps import load_aps
 from firefly.bss import BssDisplay
+from haven.instrument.aps import load_aps
 
 
 @pytest.fixture()
@@ -13,25 +14,29 @@ def bss_api(mocker):
     api = mocker.MagicMock()
     api.getCurrentEsafs.return_value = [
         {
-            "description": "We will perform some K-edge and L-edge XAFS measurements of "
-            "some transition metal nanoparticle powder samples such as "
-            "silver, palladium, gold, copper and platinum. \r\n"
-            "\r\n"
-            "Some of the measurements will need in situ gas adsorption "
-            "and/or heating conditions. Standard beamline temperature "
-            "controller and power supply will be used to heat samples to "
-            "100C. Some of the samples may be in solution phase. We will "
-            "also perform Au L2 edge XAFS measurement in HERFD mode of "
-            "some metal nanoparticle samples. The nanoparticle samples "
-            "are all unbound nanostructured materials. Samples will be "
-            "encapsulated in Kapton tape or prepared by using capillaries "
-            "and quartz wool. For solution phase measurements, the Teflon "
-            "sample holder will be used.",
+            "description": (
+                "We will perform some K-edge and L-edge XAFS measurements of "
+                "some transition metal nanoparticle powder samples such as "
+                "silver, palladium, gold, copper and platinum. \r\n"
+                "\r\n"
+                "Some of the measurements will need in situ gas adsorption "
+                "and/or heating conditions. Standard beamline temperature "
+                "controller and power supply will be used to heat samples to "
+                "100C. Some of the samples may be in solution phase. We will "
+                "also perform Au L2 edge XAFS measurement in HERFD mode of "
+                "some metal nanoparticle samples. The nanoparticle samples "
+                "are all unbound nanostructured materials. Samples will be "
+                "encapsulated in Kapton tape or prepared by using capillaries "
+                "and quartz wool. For solution phase measurements, the Teflon "
+                "sample holder will be used."
+            ),
             "esafId": 269238,
             "esafStatus": "Pending",
-            "esafTitle": "A Partner User Proposal to Continue the Successful "
-            "Collaboration between the Canadian Light Source Inc. and the "
-            "Advanced Photon Source",
+            "esafTitle": (
+                "A Partner User Proposal to Continue the Successful "
+                "Collaboration between the Canadian Light Source Inc. and the "
+                "Advanced Photon Source"
+            ),
             "experimentEndDate": "2023-03-31 08:00:00",
             "experimentStartDate": "2023-03-28 08:00:00",
             "experimentUsers": [
@@ -74,7 +79,10 @@ def bss_api(mocker):
 
     api.getCurrentProposals.return_value = [
         {
-            "title": "A Partner User Proposal to Continue the Successful Collaboration between the Canadian Light Source Inc. and the Advanced Photon Source",
+            "title": (
+                "A Partner User Proposal to Continue the Successful Collaboration"
+                " between the Canadian Light Source Inc. and the Advanced Photon Source"
+            ),
             "id": 74163,
             "experimenters": [
                 {
diff --git a/src/firefly/tests/test_cameras_display.py b/src/firefly/tests/test_cameras_display.py
index 3289021b..234bd4ec 100644
--- a/src/firefly/tests/test_cameras_display.py
+++ b/src/firefly/tests/test_cameras_display.py
@@ -1,14 +1,13 @@
-import pytest
 import json
 
-import haven
+import pytest
 from pydm.data_plugins.epics_plugin import EPICSPlugin
 from pydm.widgets.channel import PyDMChannel
-from qtpy import QtWidgets, QtGui, QtCore
+from qtpy import QtCore, QtGui, QtWidgets
 
-from firefly.cameras import CamerasDisplay
+import haven
 from firefly.camera import CameraDisplay, DetectorStates
-
+from firefly.cameras import CamerasDisplay
 
 macros = {"PREFIX": "camera_ioc:", "DESC": "Camera A"}
 
diff --git a/src/firefly/tests/test_count_window.py b/src/firefly/tests/test_count_window.py
index 1887c3d7..491275dd 100644
--- a/src/firefly/tests/test_count_window.py
+++ b/src/firefly/tests/test_count_window.py
@@ -1,7 +1,7 @@
 from unittest import mock
 
-from qtpy import QtCore
 from bluesky_queueserver_api import BPlan
+from qtpy import QtCore
 
 from firefly.plans.count import CountDisplay
 
diff --git a/src/firefly/tests/test_energy_display.py b/src/firefly/tests/test_energy_display.py
index d67ba188..5ac70962 100644
--- a/src/firefly/tests/test_energy_display.py
+++ b/src/firefly/tests/test_energy_display.py
@@ -2,27 +2,26 @@
 from unittest import mock
 
 import pytest
-from qtpy import QtWidgets, QtCore
-from bluesky_queueserver_api import BPlan
 from apstools.devices.aps_undulator import ApsUndulator
+from bluesky_queueserver_api import BPlan
 from ophyd.sim import make_fake_device
+from qtpy import QtCore, QtWidgets
 
 import haven
-from haven.instrument.monochromator import load_monochromator
-from haven.instrument.energy_positioner import load_energy_positioner
 from firefly.energy import EnergyDisplay
-
+from haven.instrument.energy_positioner import load_energy_positioner
+from haven.instrument.monochromator import load_monochromator
 
 FakeMonochromator = make_fake_device(haven.instrument.monochromator.Monochromator)
-FakeEnergyPositioner = make_fake_device(haven.instrument.energy_positioner.EnergyPositioner)
+FakeEnergyPositioner = make_fake_device(
+    haven.instrument.energy_positioner.EnergyPositioner
+)
 FakeUndulator = make_fake_device(ApsUndulator)
 
 
 def test_mono_caqtdm_macros(qtbot, ffapp, sim_registry):
     # Create fake device
-    mono = sim_registry.register(
-        FakeMonochromator("mono_ioc", name="monochromator")
-    )
+    mono = sim_registry.register(FakeMonochromator("mono_ioc", name="monochromator"))
     sim_registry.register(
         FakeEnergyPositioner(
             mono_pv="mono_ioc:Energy",
@@ -54,9 +53,7 @@ def test_mono_caqtdm_macros(qtbot, ffapp, sim_registry):
 
 def test_id_caqtdm_macros(qtbot, ffapp, sim_registry):
     # Create fake device
-    mono = FakeMonochromator(
-        "mono_ioc", name="monochromator"
-    )
+    mono = FakeMonochromator("mono_ioc", name="monochromator")
     sim_registry.register(
         FakeEnergyPositioner(
             mono_pv="mono_ioc:Energy",
@@ -82,9 +79,7 @@ def test_id_caqtdm_macros(qtbot, ffapp, sim_registry):
 
 
 def test_move_energy(qtbot, ffapp, sim_registry):
-    mono = FakeMonochromator(
-        "mono_ioc", name="monochromator"
-    )
+    mono = FakeMonochromator("mono_ioc", name="monochromator")
     sim_registry.register(
         FakeEnergyPositioner(
             mono_pv="mono_ioc:Energy",
@@ -112,9 +107,7 @@ def check_item(item):
 
 def test_predefined_energies(qtbot, ffapp, sim_registry):
     # Create fake device
-    mono = FakeMonochromator(
-        "mono_ioc", name="monochromator"
-    )
+    mono = FakeMonochromator("mono_ioc", name="monochromator")
     sim_registry.register(
         FakeEnergyPositioner(
             mono_pv="mono_ioc:Energy",
diff --git a/src/firefly/tests/test_main_window.py b/src/firefly/tests/test_main_window.py
index e8c021de..39e95043 100644
--- a/src/firefly/tests/test_main_window.py
+++ b/src/firefly/tests/test_main_window.py
@@ -1,10 +1,10 @@
+import time
 from unittest import mock
 
-import time
 import pytest
 
-from firefly.main_window import FireflyMainWindow, PlanMainWindow
 from firefly.application import FireflyApplication
+from firefly.main_window import FireflyMainWindow, PlanMainWindow
 
 
 def test_navbar(ffapp):
diff --git a/src/firefly/tests/test_motor_menu.py b/src/firefly/tests/test_motor_menu.py
index 01f72469..aa623203 100644
--- a/src/firefly/tests/test_motor_menu.py
+++ b/src/firefly/tests/test_motor_menu.py
@@ -1,16 +1,16 @@
-import time
-import pytest
-import logging
 import asyncio
+import logging
+import time
 from unittest import mock
 
 import epics
-from haven.instrument import motor, registry
-from qtpy import QtWidgets
+import pytest
 from ophyd.sim import instantiate_fake_device, make_fake_device
+from qtpy import QtWidgets
 
-from firefly.main_window import FireflyMainWindow
 from firefly.application import FireflyApplication
+from firefly.main_window import FireflyMainWindow
+from haven.instrument import motor, registry
 
 
 @pytest.fixture
diff --git a/src/firefly/tests/test_ophyd_connection.py b/src/firefly/tests/test_ophyd_connection.py
index 9e71ecf1..1d5b59f4 100644
--- a/src/firefly/tests/test_ophyd_connection.py
+++ b/src/firefly/tests/test_ophyd_connection.py
@@ -1,13 +1,13 @@
 import time
-import pytest
 from unittest.mock import MagicMock
 
-from ophyd import EpicsSignal, EpicsMotor, sim
+import pytest
+from ophyd import EpicsMotor, EpicsSignal, sim
 from ophyd.sim import instantiate_fake_device, make_fake_device
-from pydm.data_plugins import plugin_for_address, add_plugin
+from pydm import PyDMChannel
+from pydm.data_plugins import add_plugin, plugin_for_address
 from pydm.main_window import PyDMMainWindow
 from pydm.widgets import PyDMLineEdit
-from pydm import PyDMChannel
 from qtpy import QtCore
 from typhos.plugins.core import SignalPlugin
 
@@ -25,7 +25,7 @@ def sim_motor(sim_registry):
     motor = FakeMotor("255idVME:m1", name="motor")
     motor.user_setpoint.sim_set_limits((0, 1000))
     sim_registry.register(motor)
-    
+
     return motor
 
 
diff --git a/src/firefly/tests/test_queue_client.py b/src/firefly/tests/test_queue_client.py
index 70f54126..a08153af 100644
--- a/src/firefly/tests/test_queue_client.py
+++ b/src/firefly/tests/test_queue_client.py
@@ -1,20 +1,20 @@
-import time
-import pytest
-from unittest.mock import MagicMock
 import asyncio
+import time
 from collections import ChainMap
+from unittest.mock import MagicMock
 
-from bluesky import RunEngine, plans as bp
-from qtpy.QtCore import QThread
-from qtpy.QtTest import QSignalSpy
-from qtpy.QtWidgets import QAction
+import pytest
+from bluesky import RunEngine
+from bluesky import plans as bp
 from bluesky_queueserver_api import BPlan
 from bluesky_queueserver_api.zmq import REManagerAPI
 from pytestqt.exceptions import TimeoutError
+from qtpy.QtCore import QThread
+from qtpy.QtTest import QSignalSpy
+from qtpy.QtWidgets import QAction
 
-from firefly.queue_client import QueueClient
 from firefly.application import REManagerAPI
-
+from firefly.queue_client import QueueClient
 
 qs_status = {
     "msg": "RE Manager v0.0.18",
@@ -222,7 +222,9 @@ def client():
     api = MagicMock()
     api.queue_start.return_value = {"success": True}
     api.status.return_value = qs_status
-    api.queue_start.return_value = {"success": True,}
+    api.queue_start.return_value = {
+        "success": True,
+    }
     api.devices_allowed.return_value = {"success": True, "devices_allowed": {}}
     api.environment_open.return_value = {"success": True}
     api.environment_close.return_value = {"success": True}
@@ -231,7 +233,11 @@ def client():
     autoplay_action.setCheckable(True)
     open_environment_action = QAction()
     open_environment_action.setCheckable(True)
-    client = QueueClient(api=api, autoplay_action=autoplay_action, open_environment_action=open_environment_action)
+    client = QueueClient(
+        api=api,
+        autoplay_action=autoplay_action,
+        open_environment_action=open_environment_action,
+    )
     yield client
 
 
@@ -339,7 +345,7 @@ def test_open_environment(client, qtbot):
 def test_devices_available(client, qtbot):
     """Check that the queue client provides a list of devices that can be
     used in plans.
-    
+
     """
     api = client.api
     api.devices_allowed.return_value = devices_allowed
diff --git a/src/firefly/tests/test_run_browser.py b/src/firefly/tests/test_run_browser.py
index a4cc6a5f..9a3b8d4e 100644
--- a/src/firefly/tests/test_run_browser.py
+++ b/src/firefly/tests/test_run_browser.py
@@ -1,24 +1,23 @@
+import logging
 import time
-from unittest.mock import MagicMock
 from collections import namedtuple
-import logging
-import pytest
+from unittest.mock import MagicMock
 
-from qtpy.QtCore import Qt
-from pyqtgraph import PlotItem, PlotWidget
 import numpy as np
 import pandas as pd
-from tiled.adapters.mapping import MapAdapter
+import pytest
+from pyqtgraph import PlotItem, PlotWidget
+from qtpy.QtCore import Qt
 from tiled.adapters.array import ArrayAdapter
+from tiled.adapters.mapping import MapAdapter
 from tiled.adapters.xarray import DatasetAdapter
-from tiled.server.app import build_app
 from tiled.client import Context, from_context
+from tiled.server.app import build_app
 
-from haven import tiled_client
 from firefly.main_window import PlanMainWindow
 from firefly.run_browser import RunBrowserDisplay
 from firefly.run_client import DatabaseWorker
-
+from haven import tiled_client
 
 log = logging.getLogger(__name__)
 
@@ -27,6 +26,7 @@ def wait_for_runs_model(display, qtbot):
     with qtbot.waitSignal(display.runs_model_changed):
         pass
 
+
 # Some mocked test data
 run1 = pd.DataFrame(
     {
@@ -92,7 +92,7 @@ def client():
     with Context.from_app(app) as context:
         client = from_context(context)
         yield client["255id_testing"]
-        
+
 
 @pytest.fixture()
 def display(ffapp, client, qtbot):
diff --git a/src/firefly/tests/test_tiled_server.py b/src/firefly/tests/test_tiled_server.py
index 12a0d8cb..504b23d9 100644
--- a/src/firefly/tests/test_tiled_server.py
+++ b/src/firefly/tests/test_tiled_server.py
@@ -1,14 +1,12 @@
 """Tests to check that the simulated tiled client works properly."""
 
-import pytest
-
 import numpy
-from tiled.adapters.mapping import MapAdapter
+import pytest
 from tiled.adapters.array import ArrayAdapter
+from tiled.adapters.mapping import MapAdapter
 from tiled.adapters.xarray import DatasetAdapter
-from tiled.server.app import build_app
 from tiled.client import Context, from_context
-
+from tiled.server.app import build_app
 
 simple_tree = MapAdapter(
     {
diff --git a/src/firefly/tests/test_voltmeters.py b/src/firefly/tests/test_voltmeters.py
index 272ef07a..468ff0f7 100644
--- a/src/firefly/tests/test_voltmeters.py
+++ b/src/firefly/tests/test_voltmeters.py
@@ -1,13 +1,12 @@
 import time
-
 from unittest import mock
+
 import pytest
 from qtpy import QtWidgets
 
 import haven
-
-from firefly.main_window import FireflyMainWindow
 from firefly.application import FireflyApplication
+from firefly.main_window import FireflyMainWindow
 from firefly.voltmeter import VoltmeterDisplay
 from firefly.voltmeters import VoltmetersDisplay
 
diff --git a/src/firefly/tests/test_xrf_detector_display.py b/src/firefly/tests/test_xrf_detector_display.py
index 531f3ef4..0b63b68f 100644
--- a/src/firefly/tests/test_xrf_detector_display.py
+++ b/src/firefly/tests/test_xrf_detector_display.py
@@ -1,16 +1,15 @@
 import time
-from ophyd import Kind
 
 import numpy as np
-from pyqtgraph import PlotItem
 import pytest
+from ophyd import Kind
+from pyqtgraph import PlotItem
 from qtpy import QtCore
 
 from firefly.xrf_detector import XRFDetectorDisplay, XRFPlotWidget
 from firefly.xrf_roi import XRFROIDisplay
 
-
-detectors = ["dxp", 'xspress']
+detectors = ["dxp", "xspress"]
 
 
 @pytest.fixture()
diff --git a/src/firefly/voltmeter.py b/src/firefly/voltmeter.py
index 6d898316..5dfea5da 100644
--- a/src/firefly/voltmeter.py
+++ b/src/firefly/voltmeter.py
@@ -1,12 +1,12 @@
 import logging
-from typing import Sequence, Mapping, Optional
+from typing import Mapping, Optional, Sequence
 
-from pydm.widgets.channel import PyDMChannel
-from haven.instrument.ion_chamber import IonChamber
-from haven import load_config, exceptions, registry
 import qtawesome as qta
+from pydm.widgets.channel import PyDMChannel
 
-from firefly import display, FireflyApplication
+from firefly import FireflyApplication, display
+from haven import exceptions, load_config, registry
+from haven.instrument.ion_chamber import IonChamber
 
 log = logging.getLogger(__name__)
 
diff --git a/src/firefly/voltmeters.py b/src/firefly/voltmeters.py
index 103ec266..0ec7be8c 100644
--- a/src/firefly/voltmeters.py
+++ b/src/firefly/voltmeters.py
@@ -1,13 +1,13 @@
 import json
-import warnings
 import logging
-from typing import Optional, Mapping, Sequence
+import warnings
+from typing import Mapping, Optional, Sequence
 
+import qtawesome as qta
 from pydm.widgets import PyDMEmbeddedDisplay
 from qtpy import QtWidgets
-import haven
-import qtawesome as qta
 
+import haven
 from firefly import display
 
 # from .voltmeter import VoltmeterDisplay
diff --git a/src/firefly/xrf_detector.py b/src/firefly/xrf_detector.py
index ea8b8302..f2c1bc27 100644
--- a/src/firefly/xrf_detector.py
+++ b/src/firefly/xrf_detector.py
@@ -1,30 +1,29 @@
-import time
+import gc
+import json
 import logging
 import subprocess
-from pathlib import Path
-from typing import Sequence, Optional
-import json
-from contextlib import contextmanager
-from functools import partial
+import sys
+import time
 from collections import defaultdict
-import gc
+from contextlib import contextmanager
 from enum import IntEnum
+from functools import partial
+from pathlib import Path
+from typing import Optional, Sequence
 
-from qtpy import uic
-from qtpy.QtCore import Qt, Signal, QObject, QThread
-from qtpy.QtWidgets import QWidget
-import qtawesome as qta
-import pyqtgraph
-import pydm
-from pydm.widgets import PyDMEmbeddedDisplay, PyDMChannel
-import numpy as np
 import matplotlib.pyplot as plt
+import numpy as np
+import pydm
+import pyqtgraph
+import qtawesome as qta
 from matplotlib.colors import TABLEAU_COLORS
+from pydm.widgets import PyDMChannel, PyDMEmbeddedDisplay
+from qtpy import uic
+from qtpy.QtCore import QObject, Qt, QThread, Signal
+from qtpy.QtWidgets import QWidget
 
 import haven
-from firefly import display, FireflyApplication
-
-import sys
+from firefly import FireflyApplication, display
 
 np.set_printoptions(threshold=sys.maxsize)
 
@@ -98,7 +97,7 @@ def set_region_lower(self, new_lower):
         if new_lower == self._last_lower:
             return
         log.debug(
-            "Setting new region lower bound: " f"{new_lower} from {self._last_lower}"
+            f"Setting new region lower bound: {new_lower} from {self._last_lower}"
         )
         self._last_lower = new_lower
         self.blockLineSignal = True
@@ -110,7 +109,7 @@ def set_region_upper(self, new_upper):
         if new_upper == self._last_upper:
             return
         log.debug(
-            "Setting new region upper bound: " f"{new_upper} from {self._last_upper}"
+            f"Setting new region upper bound: {new_upper} from {self._last_upper}"
         )
         self._last_upper = new_upper
         self.blockLineSignal = True
@@ -266,7 +265,8 @@ def highlight_spectrum(self, mca_num, roi_num, hovered):
             elif self._selected_spectrum is not None:
                 # Highlight the spectrum that was previously selected
                 log.debug(
-                    f"Reverting to previously selected spectrum: {self._selected_spectrum}"
+                    "Reverting to previously selected spectrum:"
+                    f" {self._selected_spectrum}"
                 )
                 is_dimmed = key != self._selected_spectrum
                 if is_dimmed:
diff --git a/src/haven/__init__.py b/src/haven/__init__.py
index 7ea95c2d..fb1469f5 100644
--- a/src/haven/__init__.py
+++ b/src/haven/__init__.py
@@ -2,50 +2,49 @@
 
 __version__ = "0.1.0"
 
+from ._iconfig import load_config  # noqa: F401
+
 #  Top-level imports
-from .catalog import load_catalog, load_result, load_data, tiled_client  # noqa: F401
+from .catalog import load_catalog, load_data, load_result, tiled_client  # noqa: F401
+from .constants import edge_energy
 from .energy_ranges import ERange, KRange, merge_ranges  # noqa: F401
-from .plans.energy_scan import energy_scan  # noqa: F401
+from .instrument import (  # noqa: F401
+    InstrumentRegistry,
+    IonChamber,
+    Monochromator,
+    ion_chamber,
+    registry,
+)
+from .instrument.dxp import load_dxp  # noqa: F401
+from .instrument.load_instrument import load_instrument  # noqa: F401
+from .instrument.motor import HavenMotor  # noqa: F401
+from .instrument.xspress import load_xspress  # noqa: F401
+from .motor_position import (  # noqa: F401
+    get_motor_position,
+    list_current_motor_positions,
+    list_motor_positions,
+    recall_motor_position,
+    save_motor_position,
+)
+from .plans.align_motor import align_motor, align_pitch2  # noqa: F401
 from .plans.align_slits import align_slits  # noqa: F401
-from .plans.beam_properties import knife_scan, fit_step  # noqa: F401
-from .plans.xafs_scan import xafs_scan  # noqa: F401
+from .plans.auto_gain import AutoGainCallback, auto_gain  # noqa:F401
+from .plans.beam_properties import fit_step  # noqa: F401
 from .plans.beam_properties import knife_scan  # noqa: F401
-from .plans.auto_gain import auto_gain, AutoGainCallback  # noqa:F401
+from .plans.energy_scan import energy_scan  # noqa: F401
+from .plans.fly import fly_scan, grid_fly_scan  # noqa: F401
 from .plans.mono_gap_calibration import calibrate_mono_gap  # noqa: F401
 from .plans.mono_ID_calibration import mono_ID_calibration  # noqa: F401
-from .plans.set_energy import set_energy  # noqa: F401
-from .plans.align_motor import align_motor, align_pitch2  # noqa: F401
-from .plans.shutters import open_shutters, close_shutters  # noqa: F401
 from .plans.record_dark_current import record_dark_current  # noqa: F401
-from .plans.fly import fly_scan, grid_fly_scan  # noqa: F401
-from .run_engine import run_engine  # noqa: F401
-from ._iconfig import load_config  # noqa: F401
-from .preprocessors import (
+from .plans.set_energy import set_energy  # noqa: F401
+from .plans.shutters import close_shutters, open_shutters  # noqa: F401
+from .plans.xafs_scan import xafs_scan  # noqa: F401
+from .preprocessors import (  # noqa: F401
     baseline_decorator,
     baseline_wrapper,
-    shutter_suspend_wrapper,
     shutter_suspend_decorator,
-)  # noqa: F401
-from .constants import edge_energy
-
-from .motor_position import (  # noqa: F401
-    save_motor_position,
-    list_motor_positions,
-    get_motor_position,
-    recall_motor_position,
-    list_current_motor_positions,
-)
-from .instrument import (  # noqa: F401
-    ion_chamber,
-    IonChamber,
-    InstrumentRegistry,
-    registry,
-    Monochromator,
+    shutter_suspend_wrapper,
 )
-from .instrument.load_instrument import load_instrument  # noqa: F401
-from .instrument.motor import HavenMotor  # noqa: F401
-from .instrument.dxp import load_dxp  # noqa: F401
-from .instrument.xspress import load_xspress  # noqa: F401
-
-from .xdi_writer import XDIWriter  # noqa: F401
 from .progress_bar import ProgressBar  # noqa: F401
+from .run_engine import run_engine  # noqa: F401
+from .xdi_writer import XDIWriter  # noqa: F401
diff --git a/src/haven/_iconfig.py b/src/haven/_iconfig.py
index b52d249a..529684e9 100644
--- a/src/haven/_iconfig.py
+++ b/src/haven/_iconfig.py
@@ -9,17 +9,17 @@
     "load_config",
 ]
 
-import os
+import argparse
 import logging
-from typing import Sequence
+import os
 import pathlib
-import argparse
-from functools import lru_cache
 from contextlib import contextmanager
 from copy import deepcopy
+from functools import lru_cache
+from typing import Sequence
 
-from mergedeep import merge
 import tomli
+from mergedeep import merge
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/energy_ranges.py b/src/haven/energy_ranges.py
index dda7701b..cbf4bd4a 100644
--- a/src/haven/energy_ranges.py
+++ b/src/haven/energy_ranges.py
@@ -4,7 +4,6 @@
 import pint
 from scipy import constants
 
-
 __all__ = ["ERange", "KRange"]
 
 
diff --git a/src/haven/exceptions.py b/src/haven/exceptions.py
index cc18cf6f..c4652591 100644
--- a/src/haven/exceptions.py
+++ b/src/haven/exceptions.py
@@ -1,7 +1,7 @@
 from .instrument.instrument_registry import (
     ComponentNotFound,
-    MultipleComponentsFound,
     InvalidComponentLabel,
+    MultipleComponentsFound,
 )
 
 
diff --git a/src/haven/instrument/__init__.py b/src/haven/instrument/__init__.py
index dd4eebd1..bab75b36 100644
--- a/src/haven/instrument/__init__.py
+++ b/src/haven/instrument/__init__.py
@@ -1,4 +1,4 @@
+from .instrument_registry import InstrumentRegistry, registry  # noqa: F401
 from .ion_chamber import IonChamber  # noqa: F401
-from .motor import HavenMotor  # noqa: F401
 from .monochromator import Monochromator  # noqa: F401
-from .instrument_registry import InstrumentRegistry, registry  # noqa: F401
+from .motor import HavenMotor  # noqa: F401
diff --git a/src/haven/instrument/aerotech.py b/src/haven/instrument/aerotech.py
index 47dc4de3..cff8803f 100644
--- a/src/haven/instrument/aerotech.py
+++ b/src/haven/instrument/aerotech.py
@@ -1,35 +1,27 @@
-import threading
-import time
-import logging
 import asyncio
+import logging
 import math
-from typing import Generator, Dict
-from datetime import datetime, timedelta
+import threading
+import time
 from collections import OrderedDict
+from datetime import datetime, timedelta
+from typing import Dict, Generator
 
-from ophyd import (
-    Device,
-    FormattedComponent as FCpt,
-    EpicsMotor,
-    Component as Cpt,
-    Signal,
-    SignalRO,
-    Kind,
-    EpicsSignal,
-    flyers,
-)
-from ophyd.status import SubscriptionStatus, AndStatus, StatusBase
-from apstools.synApps.asyn import AsynRecord
-import pint
 import numpy as np
+import pint
+from apstools.synApps.asyn import AsynRecord
+from ophyd import Component as Cpt
+from ophyd import Device, EpicsMotor, EpicsSignal
+from ophyd import FormattedComponent as FCpt
+from ophyd import Kind, Signal, SignalRO, flyers
+from ophyd.status import AndStatus, StatusBase, SubscriptionStatus
 
-from .delay import DG645Delay
-from .stage import XYStage
-from .instrument_registry import registry
 from .._iconfig import load_config
 from ..exceptions import InvalidScanParameters
-from .device import await_for_connection, aload_devices, make_device
-
+from .delay import DG645Delay
+from .device import aload_devices, await_for_connection, make_device
+from .instrument_registry import registry
+from .stage import XYStage
 
 log = logging.getLogger(__name__)
 
@@ -400,12 +392,14 @@ def _update_fly_params(self, *args, **kwargs):
             return
         if encoder_resolution == 0:
             log.warning(
-                f"{self} encoder resolution is zero. Could not update fly scan parameters."
+                f"{self} encoder resolution is zero. Could not update fly scan"
+                " parameters."
             )
             return
         if accel_time <= 0:
             log.warning(
-                f"{self} acceleration is non-positive. Could not update fly scan parameters."
+                f"{self} acceleration is non-positive. Could not update fly scan"
+                " parameters."
             )
             return
         # Determine the desired direction of travel and overal sense
@@ -506,7 +500,8 @@ def check_flyscan_bounds(self):
             taxi_distance = abs(taxi.get() - pso.get())
             if taxi_distance > (1.1 * step_size):
                 raise InvalidScanParameters(
-                    f"Scan parameters for {taxi}, {pso}, {self.step_size} would produce extra pulses without a window."
+                    f"Scan parameters for {taxi}, {pso}, {self.step_size} would produce"
+                    " extra pulses without a window."
                 )
 
     def enable_pso(self):
@@ -610,4 +605,3 @@ def load_aerotech_stage_coros(config=None):
 def load_aerotech_stages(config=None):
     """Load the XY stages defined in the config ``[stage]`` section."""
     asyncio.run(aload_devices(*load_aerotech_stage_coros(config=config)))
-        
diff --git a/src/haven/instrument/aps.py b/src/haven/instrument/aps.py
index 29f6f6a2..574aff73 100644
--- a/src/haven/instrument/aps.py
+++ b/src/haven/instrument/aps.py
@@ -1,15 +1,15 @@
+import asyncio
 import logging
 import warnings
-import asyncio
 
-from apstools.devices.aps_machine import ApsMachineParametersDevice
 from apsbss.apsbss_ophyd import EpicsBssDevice
+from apstools.devices.aps_machine import ApsMachineParametersDevice
 
 from haven import registry
+
 from .._iconfig import load_config
 from .device import aload_devices, make_device
 
-
 log = logging.getLogger(__name__)
 
 
diff --git a/src/haven/instrument/area_detector.py b/src/haven/instrument/area_detector.py
index ee7dc779..f2ed8731 100644
--- a/src/haven/instrument/area_detector.py
+++ b/src/haven/instrument/area_detector.py
@@ -1,41 +1,39 @@
-import logging
 import asyncio
+import logging
 from enum import IntEnum
 
 from apstools.devices import CamMixin_V34, SingleTrigger_V34
+from ophyd import ADComponent as ADCpt
+from ophyd import DetectorBase as OphydDetectorBase
 from ophyd import (
-    ADComponent as ADCpt,
-    DetectorBase as OphydDetectorBase,
-    SimDetectorCam,
-    Lambda750kCam,
     EigerDetectorCam,
-    SingleTrigger,
     Kind,
+    Lambda750kCam,
     OphydObject,
+    SimDetectorCam,
+    SingleTrigger,
 )
 from ophyd.areadetector.base import EpicsSignalWithRBV as SignalWithRBV
 from ophyd.areadetector.filestore_mixins import FileStoreHDF5IterativeWrite
 from ophyd.areadetector.plugins import (
-    HDF5Plugin_V34,
     HDF5Plugin_V31,
-    ImagePlugin_V34,
+    HDF5Plugin_V34,
     ImagePlugin_V31,
-    PvaPlugin_V34,
+    ImagePlugin_V34,
+    OverlayPlugin,
     PvaPlugin_V31,
-    TIFFPlugin_V34,
-    TIFFPlugin_V31,
-    ROIPlugin_V34,
+    PvaPlugin_V34,
     ROIPlugin_V31,
-    StatsPlugin_V31 as OphydStatsPlugin_V31,
-    StatsPlugin_V34 as OphydStatsPlugin_V34,
-    OverlayPlugin,
+    ROIPlugin_V34,
 )
+from ophyd.areadetector.plugins import StatsPlugin_V31 as OphydStatsPlugin_V31
+from ophyd.areadetector.plugins import StatsPlugin_V34 as OphydStatsPlugin_V34
+from ophyd.areadetector.plugins import TIFFPlugin_V31, TIFFPlugin_V34
 
+from .. import exceptions
 from .._iconfig import load_config
+from .device import aload_devices, await_for_connection, make_device
 from .instrument_registry import registry
-from .device import make_device
-from .. import exceptions
-from .device import await_for_connection, aload_devices
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/camera.py b/src/haven/instrument/camera.py
index 06d3be92..af41693f 100644
--- a/src/haven/instrument/camera.py
+++ b/src/haven/instrument/camera.py
@@ -3,29 +3,27 @@
 import warnings
 from typing import Optional, Sequence
 
+from ophyd import ADComponent as ADCpt
 from ophyd import (
     CamBase,
-    SingleTrigger,
-    Kind,
-    ADComponent as ADCpt,
     EpicsSignal,
+    Kind,
+    SingleTrigger,
     do_not_wait_for_lazy_connection,
 )
 from ophyd.areadetector.base import EpicsSignalWithRBV as SignalWithRBV
 from ophyd.areadetector.plugins import (
     ImagePlugin_V34,
-    PvaPlugin_V34,
     OverlayPlugin,
+    PvaPlugin_V34,
     ROIPlugin_V34,
 )
 
-
-from .instrument_registry import registry
-from .area_detector import DetectorBase, StatsPlugin_V34, SimDetector, AsyncCamMixin
-from .device import await_for_connection, aload_devices, make_device
-from .._iconfig import load_config
 from .. import exceptions
-
+from .._iconfig import load_config
+from .area_detector import AsyncCamMixin, DetectorBase, SimDetector, StatsPlugin_V34
+from .device import aload_devices, await_for_connection, make_device
+from .instrument_registry import registry
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/delay.py b/src/haven/instrument/delay.py
index 01f40823..710751ba 100644
--- a/src/haven/instrument/delay.py
+++ b/src/haven/instrument/delay.py
@@ -1,6 +1,7 @@
 import enum
 
-from ophyd import Device, Component as Cpt, EpicsSignal, EpicsSignalRO, Kind
+from ophyd import Component as Cpt
+from ophyd import Device, EpicsSignal, EpicsSignalRO, Kind
 
 
 class EpicsSignalWithIO(EpicsSignal):
diff --git a/src/haven/instrument/device.py b/src/haven/instrument/device.py
index 94a7222b..4b99681b 100644
--- a/src/haven/instrument/device.py
+++ b/src/haven/instrument/device.py
@@ -1,14 +1,14 @@
+import asyncio
 import logging
 import re
 import time as ttime
 from typing import Callable, Union
-import asyncio
 
-from ophyd import Component, K, Device
+from ophyd import Component, Device, K
 from ophyd.sim import make_fake_device
-from .instrument_registry import registry
-from .._iconfig import load_config
 
+from .._iconfig import load_config
+from .instrument_registry import registry
 
 log = logging.getLogger(__name__)
 
@@ -64,7 +64,8 @@ async def make_device(DeviceClass, *args, FakeDeviceClass=None, **kwargs) -> Dev
         if DeviceClass.__name__ == "VortexEx":
             raise
         log.warning(
-            f"Could not connect to {DeviceClass.__name__} in {round(ttime.monotonic() - t0, 2)} sec: {name}."
+            f"Could not connect to {DeviceClass.__name__} in"
+            f" {round(ttime.monotonic() - t0, 2)} sec: {name}."
         )
         log.info(f"Reason for {name} failure: {e}.")
         return None
diff --git a/src/haven/instrument/dxp.py b/src/haven/instrument/dxp.py
index a9c89502..824c4ba2 100644
--- a/src/haven/instrument/dxp.py
+++ b/src/haven/instrument/dxp.py
@@ -1,54 +1,41 @@
-from enum import IntEnum
-from collections import OrderedDict
-from typing import Optional, Sequence
-import warnings
-import logging
 import asyncio
+import logging
 import time
+import warnings
+from collections import OrderedDict
+from enum import IntEnum
+from typing import Optional, Sequence
 
-from ophyd import (
-    ADComponent as ADCpt,
-    OphydObject,
-    mca,
-    Device,
-    EpicsSignal,
-    EpicsSignalRO,
-    Component as Cpt,
-    DynamicDeviceComponent as DDC,
-    Kind,
-    Signal,
-    flyers,
-)
-from ophyd.areadetector.plugins import NetCDFPlugin_V34
-from ophyd.status import SubscriptionStatus, StatusBase
-from ophyd.signal import InternalSignal, DerivedSignal
 from apstools.utils import cleanupText
+from ophyd import ADComponent as ADCpt
+from ophyd import Component as Cpt
+from ophyd import Device
+from ophyd import DynamicDeviceComponent as DDC
+from ophyd import EpicsSignal, EpicsSignalRO, Kind, OphydObject, Signal, flyers, mca
+from ophyd.areadetector.plugins import NetCDFPlugin_V34
 from ophyd.pseudopos import (
     PseudoPositioner,
     PseudoSingle,
     pseudo_position_argument,
     real_position_argument,
 )
+from ophyd.signal import DerivedSignal, InternalSignal
+from ophyd.status import StatusBase, SubscriptionStatus
 
-from .scaler_triggered import ScalerTriggered
-from .instrument_registry import registry
+from .. import exceptions
+from .._iconfig import load_config
+from .device import RegexComponent as RECpt
+from .device import aload_devices, await_for_connection, make_device
 from .fluorescence_detector import (
+    MCASumMixin,
+    ROIMixin,
+    UseROISignal,
     XRFMixin,
     active_kind,
-    ROIMixin,
-    MCASumMixin,
     add_roi_sums,
-    UseROISignal,
-)
-from .device import (
-    RegexComponent as RECpt,
-    await_for_connection,
-    aload_devices,
-    make_device,
 )
-from .._iconfig import load_config
-from .. import exceptions
-
+from .instrument_registry import registry
+from .scaler_triggered import ScalerTriggered
 
 __all__ = ["DxpDetector", "load_dxp"]
 
diff --git a/src/haven/instrument/energy_positioner.py b/src/haven/instrument/energy_positioner.py
index 53ba9ba2..ae92b1ba 100644
--- a/src/haven/instrument/energy_positioner.py
+++ b/src/haven/instrument/energy_positioner.py
@@ -1,24 +1,17 @@
 import asyncio
 import logging
 
-from ophyd import (
-    PseudoPositioner,
-    EpicsMotor,
-    Component as Cpt,
-    FormattedComponent as FCpt,
-    PseudoSingle,
-    PVPositioner,
-    EpicsSignal,
-    EpicsSignalRO,
-)
+from ophyd import Component as Cpt
+from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO
+from ophyd import FormattedComponent as FCpt
+from ophyd import PseudoPositioner, PseudoSingle, PVPositioner
 from ophyd.ophydobj import OphydObject
 from ophyd.pseudopos import pseudo_position_argument, real_position_argument
 
 from .._iconfig import load_config
+from .device import aload_devices, await_for_connection
 from .instrument_registry import registry
-from .monochromator import Monochromator, IDTracking
-from .device import await_for_connection, aload_devices
-
+from .monochromator import IDTracking, Monochromator
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/fluorescence_detector.py b/src/haven/instrument/fluorescence_detector.py
index bba157e6..ab1d30c2 100644
--- a/src/haven/instrument/fluorescence_detector.py
+++ b/src/haven/instrument/fluorescence_detector.py
@@ -6,46 +6,33 @@
 
 """
 
-from enum import IntEnum
-from collections import OrderedDict
-from typing import Optional, Sequence
-from contextlib import contextmanager
-import warnings
-import logging
 import asyncio
+import logging
 import time
+import warnings
+from collections import OrderedDict
+from contextlib import contextmanager
+from enum import IntEnum
+from typing import Optional, Sequence
 
 import numpy as np
-from ophyd import (
-    mca,
-    Device,
-    EpicsSignal,
-    EpicsSignalRO,
-    Component as Cpt,
-    DynamicDeviceComponent as DDC,
-    Kind,
-    Signal,
-    SignalRO,
-    flyers,
-)
-from ophyd.signal import InternalSignal, DerivedSignal
-from ophyd.areadetector.plugins import NetCDFPlugin_V34
-from ophyd.status import SubscriptionStatus, StatusBase
 from apstools.utils import cleanupText
+from ophyd import Component as Cpt
+from ophyd import Device
+from ophyd import DynamicDeviceComponent as DDC
+from ophyd import EpicsSignal, EpicsSignalRO, Kind, Signal, SignalRO, flyers, mca
+from ophyd.areadetector.plugins import NetCDFPlugin_V34
+from ophyd.signal import DerivedSignal, InternalSignal
+from ophyd.status import StatusBase, SubscriptionStatus
 from pcdsdevices.signal import MultiDerivedSignal, MultiDerivedSignalRO
-from pcdsdevices.type_hints import SignalToValue, OphydDataType
+from pcdsdevices.type_hints import OphydDataType, SignalToValue
 
-from .scaler_triggered import ScalerTriggered
-from .instrument_registry import registry
-from .device import (
-    RegexComponent as RECpt,
-    await_for_connection,
-    aload_devices,
-    make_device,
-)
-from .._iconfig import load_config
 from .. import exceptions
-
+from .._iconfig import load_config
+from .device import RegexComponent as RECpt
+from .device import aload_devices, await_for_connection, make_device
+from .instrument_registry import registry
+from .scaler_triggered import ScalerTriggered
 
 __all__ = ["XRFMixin"]
 
diff --git a/src/haven/instrument/heater.py b/src/haven/instrument/heater.py
index f4a1180c..429ddc2b 100644
--- a/src/haven/instrument/heater.py
+++ b/src/haven/instrument/heater.py
@@ -1,17 +1,14 @@
 import asyncio
 import logging
 
-from ophyd import PVPositioner, EpicsSignalRO, EpicsSignalWithRBV, Component as Cpt
-from apstools.devices import (
-    PTC10PositionerMixin,
-    PTC10AioChannel as PTC10AioChannelBase,
-    PTC10TcChannel,
-)
+from apstools.devices import PTC10AioChannel as PTC10AioChannelBase
+from apstools.devices import PTC10PositionerMixin, PTC10TcChannel
+from ophyd import Component as Cpt
+from ophyd import EpicsSignalRO, EpicsSignalWithRBV, PVPositioner
 
 from .._iconfig import load_config
+from .device import aload_devices, await_for_connection, make_device
 from .instrument_registry import registry
-from .device import await_for_connection, aload_devices, make_device
-
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/instrument_registry.py b/src/haven/instrument/instrument_registry.py
index 7574e511..f51ba215 100644
--- a/src/haven/instrument/instrument_registry.py
+++ b/src/haven/instrument/instrument_registry.py
@@ -1,15 +1,14 @@
-from typing import Optional, Sequence
 import logging
 import warnings
 from itertools import chain
-from typing import Sequence
+from typing import Optional, Sequence
 
 from ophyd import ophydobj
 from ophydregistry import Registry as InstrumentRegistry
 from ophydregistry.exceptions import (
     ComponentNotFound,
-    MultipleComponentsFound,
     InvalidComponentLabel,
+    MultipleComponentsFound,
 )
 
 log = logging.getLogger(__name__)
diff --git a/src/haven/instrument/ion_chamber.py b/src/haven/instrument/ion_chamber.py
index 195694aa..4ce8be25 100644
--- a/src/haven/instrument/ion_chamber.py
+++ b/src/haven/instrument/ion_chamber.py
@@ -1,49 +1,46 @@
 """Holds ion chamber detector descriptions and assignments to EPICS PVs."""
 
-from typing import Sequence, Generator, Dict
-import logging
 import asyncio
-from collections import OrderedDict
+import logging
+import math
 import time
 import warnings
-import math
+from collections import OrderedDict
+from typing import Dict, Generator, Sequence
 
 import epics
+import numpy as np
+import pint
+from apstools.devices import SRS570_PreAmplifier
+from ophyd import Component as Cpt
+from ophyd import Device, EpicsSignal, EpicsSignalRO
+from ophyd import FormattedComponent as FCpt
 from ophyd import (
-    Device,
-    status,
-    Signal,
-    EpicsSignal,
-    EpicsSignalRO,
-    PVPositionerPC,
-    PVPositioner,
+    Kind,
     PseudoPositioner,
     PseudoSingle,
-    Component as Cpt,
-    FormattedComponent as FCpt,
-    Kind,
+    PVPositioner,
+    PVPositionerPC,
+    Signal,
     flyers,
+    status,
 )
-from ophyd.signal import InternalSignal, DerivedSignal
+from ophyd.mca import EpicsMCARecord
 from ophyd.ophydobj import OphydObject
 from ophyd.pseudopos import pseudo_position_argument, real_position_argument
-from ophyd.mca import EpicsMCARecord
+from ophyd.signal import DerivedSignal, InternalSignal
 from ophyd.status import SubscriptionStatus
 from ophyd.utils.errors import OpException
-from apstools.devices import SRS570_PreAmplifier
 from pcdsdevices.signal import MultiDerivedSignal, MultiDerivedSignalRO
-from pcdsdevices.type_hints import SignalToValue, OphydDataType
-import numpy as np
-import pint
+from pcdsdevices.type_hints import OphydDataType, SignalToValue
 
-from .scaler_triggered import ScalerTriggered, ScalerSignal, ScalerSignalRO
-from .instrument_registry import registry
+from .. import exceptions
+from .._iconfig import load_config
+from .device import aload_devices, await_for_connection, make_device
 from .epics import caget
-from .device import await_for_connection, aload_devices, make_device
+from .instrument_registry import registry
 from .labjack import AnalogInput
-from .._iconfig import load_config
-from .. import exceptions
-
+from .scaler_triggered import ScalerSignal, ScalerSignalRO, ScalerTriggered
 
 log = logging.getLogger(__name__)
 
@@ -166,7 +163,10 @@ def _put_sensitivity_level(
         new_offset = max(new_level + self.offset_difference, 0)
         # Check for out of bounds
         lmin, lmax = (0, 27)
-        msg = f"Cannot set {self.name} outside range ({lmin}, {lmax}), received {new_level}."
+        msg = (
+            f"Cannot set {self.name} outside range ({lmin}, {lmax}), received"
+            f" {new_level}."
+        )
         if new_level < lmin:
             raise exceptions.GainOverflow(msg)
         elif new_level > lmax:
@@ -610,7 +610,10 @@ async def load_ion_chamber(
     try:
         ion_chamber.voltmeter.differential.set(1).wait(timeout=1)
     except OpException as exc:
-        msg = f"Could not set voltmeter {ion_chamber.name} channel differential state: {exc}"
+        msg = (
+            f"Could not set voltmeter {ion_chamber.name} channel differential state:"
+            f" {exc}"
+        )
         log.warning(msg)
         warnings.warn(msg)
     return ion_chamber
diff --git a/src/haven/instrument/labjack.py b/src/haven/instrument/labjack.py
index 1c3d17eb..a9f51610 100644
--- a/src/haven/instrument/labjack.py
+++ b/src/haven/instrument/labjack.py
@@ -1,14 +1,9 @@
+from apstools.synApps import EpicsRecordDeviceCommonAll, EpicsRecordInputFields
+from ophyd import Component as Cpt
+from ophyd import Device, EpicsSignal, EpicsSignalRO
+from ophyd import FormattedComponent as FCpt
 from strenum import StrEnum
 
-from ophyd import (
-    Device,
-    EpicsSignal,
-    EpicsSignalRO,
-    Component as Cpt,
-    FormattedComponent as FCpt,
-)
-from apstools.synApps import EpicsRecordInputFields, EpicsRecordDeviceCommonAll
-
 
 class AnalogInput(EpicsRecordInputFields, EpicsRecordDeviceCommonAll):
     class DiffStates(StrEnum):
diff --git a/src/haven/instrument/lerix.py b/src/haven/instrument/lerix.py
index f9a8d52c..819b4c74 100644
--- a/src/haven/instrument/lerix.py
+++ b/src/haven/instrument/lerix.py
@@ -1,14 +1,16 @@
 import asyncio
 import logging
+
 import numpy as np
+from ophyd import Component as Cpt
+from ophyd import Device, EpicsMotor
+from ophyd import FormattedComponent as FCpt
+from ophyd import PseudoPositioner, PseudoSingle
 from ophyd.pseudopos import pseudo_position_argument, real_position_argument
-from ophyd import PseudoPositioner, PseudoSingle, EpicsMotor
-from ophyd import Component as Cpt, FormattedComponent as FCpt, Device
 
 from .._iconfig import load_config
+from .device import aload_devices, await_for_connection, make_device
 from .instrument_registry import registry
-from .device import await_for_connection, aload_devices, make_device
-
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/load_instrument.py b/src/haven/instrument/load_instrument.py
index 94454db3..6e8b468a 100644
--- a/src/haven/instrument/load_instrument.py
+++ b/src/haven/instrument/load_instrument.py
@@ -1,27 +1,27 @@
-from typing import Mapping
 import asyncio
+from typing import Mapping
 
 from ophyd import sim
 
-from .instrument_registry import registry as default_registry, InstrumentRegistry
+from .._iconfig import load_config
+from .aps import load_aps_coros
+from .area_detector import load_area_detector_coros
+from .camera import load_camera_coros
+from .dxp import load_dxp_coros
 from .energy_positioner import load_energy_positioner_coros
-from .motor import load_all_motor_coros, HavenMotor
+from .heater import load_heater_coros
+from .instrument_registry import InstrumentRegistry
+from .instrument_registry import registry as default_registry
 from .ion_chamber import load_ion_chamber_coros
-from .dxp import load_dxp_coros
+from .lerix import load_lerix_spectrometer_coros
 from .monochromator import load_monochromator_coros
-from .camera import load_camera_coros
+from .motor import HavenMotor, load_all_motor_coros
+from .power_supply import load_power_supply_coros
 from .shutter import load_shutter_coros
+from .slits import load_slit_coros
 from .stage import load_stage_coros
-from .aps import load_aps_coros
-from .power_supply import load_power_supply_coros
 from .xray_source import load_xray_source_coros
-from .area_detector import load_area_detector_coros
-from .slits import load_slit_coros
-from .lerix import load_lerix_spectrometer_coros
-from .heater import load_heater_coros
 from .xspress import load_xspress_coros
-from .._iconfig import load_config
-
 
 __all__ = ["load_instrument"]
 
diff --git a/src/haven/instrument/monochromator.py b/src/haven/instrument/monochromator.py
index d3e06a7f..c15e1763 100644
--- a/src/haven/instrument/monochromator.py
+++ b/src/haven/instrument/monochromator.py
@@ -1,20 +1,14 @@
 import asyncio
-from enum import IntEnum
 import logging
+from enum import IntEnum
 
-from ophyd import (
-    Device,
-    Component as Cpt,
-    FormattedComponent as FCpt,
-    EpicsMotor,
-    EpicsSignal,
-    EpicsSignalRO,
-)
+from ophyd import Component as Cpt
+from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
+from ophyd import FormattedComponent as FCpt
 
-from .instrument_registry import registry
 from .._iconfig import load_config
-from .device import await_for_connection, aload_devices, make_device
-
+from .device import aload_devices, await_for_connection, make_device
+from .instrument_registry import registry
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/motor.py b/src/haven/instrument/motor.py
index 5cf40186..ea507440 100644
--- a/src/haven/instrument/motor.py
+++ b/src/haven/instrument/motor.py
@@ -1,20 +1,20 @@
-import logging
-from typing import Optional
 import asyncio
+import contextlib
+import io
+import logging
 import time
 from functools import partial
-import io
-import contextlib
+from typing import Optional
 
 import epics
-from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO, Component as Cpt, sim
+from ophyd import Component as Cpt
+from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO, sim
 
-from .epics import caget
 from .._iconfig import load_config
-from .device import await_for_connection, aload_devices, make_device
+from .device import aload_devices, await_for_connection, make_device
+from .epics import caget
 from .instrument_registry import registry
 
-
 log = logging.getLogger(__name__)
 
 
@@ -105,7 +105,7 @@ async def load_motor(prefix: str, motor_num: int, ioc_name: str = None):
             return
     else:
         log.debug(f"Resolved motor {pv} to '{name}'")
-            
+
     # Create the motor device
     if name == f"motor {motor_num+1}":
         # It's an unnamed motor, so skip it
diff --git a/src/haven/instrument/power_supply.py b/src/haven/instrument/power_supply.py
index e341e050..4d64ab01 100644
--- a/src/haven/instrument/power_supply.py
+++ b/src/haven/instrument/power_supply.py
@@ -1,19 +1,13 @@
-import logging
 import asyncio
+import logging
 
-from ophyd import (
-    Device,
-    Component as Cpt,
-    FormattedComponent as FCpt,
-    EpicsSignal,
-    EpicsSignalRO,
-    EpicsMotor,
-)
+from ophyd import Component as Cpt
+from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
+from ophyd import FormattedComponent as FCpt
 
 from .._iconfig import load_config
+from .device import aload_devices, await_for_connection
 from .instrument_registry import registry
-from .device import await_for_connection, aload_devices
-
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/scaler_triggered.py b/src/haven/instrument/scaler_triggered.py
index f96baf2f..4c722848 100644
--- a/src/haven/instrument/scaler_triggered.py
+++ b/src/haven/instrument/scaler_triggered.py
@@ -1,11 +1,4 @@
-from ophyd import (
-    Device,
-    Component,
-    FormattedComponent,
-    EpicsSignal,
-    EpicsSignalRO,
-)
-
+from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, FormattedComponent
 
 __all__ = ["ScalerTriggered"]
 
diff --git a/src/haven/instrument/shutter.py b/src/haven/instrument/shutter.py
index 82d991bb..8fafce0f 100644
--- a/src/haven/instrument/shutter.py
+++ b/src/haven/instrument/shutter.py
@@ -1,13 +1,14 @@
-import logging
 import asyncio
-from bluesky import suspenders
-from ophyd import FormattedComponent as FCpt, EpicsSignal
+import logging
+
 from apstools.devices.shutters import ApsPssShutterWithStatus as Shutter
+from bluesky import suspenders
+from ophyd import EpicsSignal
+from ophyd import FormattedComponent as FCpt
 
 from .._iconfig import load_config
+from .device import aload_devices, await_for_connection, make_device
 from .instrument_registry import registry
-from .device import await_for_connection, aload_devices, make_device
-
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/slits.py b/src/haven/instrument/slits.py
index 2e32ec9f..a6770395 100644
--- a/src/haven/instrument/slits.py
+++ b/src/haven/instrument/slits.py
@@ -1,12 +1,11 @@
-import logging
 import asyncio
+import logging
 
 from apstools.synApps.db_2slit import Optics2Slit2D_HV
 
 from .._iconfig import load_config
+from .device import aload_devices, await_for_connection
 from .instrument_registry import registry
-from .device import await_for_connection, aload_devices
-
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/stage.py b/src/haven/instrument/stage.py
index 8e882fb6..cc23aa17 100644
--- a/src/haven/instrument/stage.py
+++ b/src/haven/instrument/stage.py
@@ -1,34 +1,26 @@
-import threading
-import time
-import logging
 import asyncio
+import logging
 import math
-from typing import Generator, Dict
-from datetime import datetime, timedelta
+import threading
+import time
 from collections import OrderedDict
+from datetime import datetime, timedelta
+from typing import Dict, Generator
 
-from ophyd import (
-    Device,
-    FormattedComponent as FCpt,
-    EpicsMotor,
-    Component as Cpt,
-    Signal,
-    SignalRO,
-    Kind,
-    EpicsSignal,
-    flyers,
-)
-from ophyd.status import SubscriptionStatus, AndStatus, StatusBase
-from apstools.synApps.asyn import AsynRecord
-import pint
 import numpy as np
+import pint
+from apstools.synApps.asyn import AsynRecord
+from ophyd import Component as Cpt
+from ophyd import Device, EpicsMotor, EpicsSignal
+from ophyd import FormattedComponent as FCpt
+from ophyd import Kind, Signal, SignalRO, flyers
+from ophyd.status import AndStatus, StatusBase, SubscriptionStatus
 
-from .delay import DG645Delay
-from .instrument_registry import registry
 from .._iconfig import load_config
 from ..exceptions import InvalidScanParameters
-from .device import await_for_connection, aload_devices, make_device
-
+from .delay import DG645Delay
+from .device import aload_devices, await_for_connection, make_device
+from .instrument_registry import registry
 
 __all__ = ["XYStage", "load_stages"]
 
@@ -36,7 +28,6 @@
 log = logging.getLogger(__name__)
 
 
-
 @registry.register
 class XYStage(Device):
     """An XY stage with two motors operating in orthogonal directions.
diff --git a/src/haven/instrument/xray_source.py b/src/haven/instrument/xray_source.py
index a14729b3..2177593d 100644
--- a/src/haven/instrument/xray_source.py
+++ b/src/haven/instrument/xray_source.py
@@ -1,12 +1,12 @@
-import logging
 import asyncio
-from apstools.devices.aps_undulator import ApsUndulator
+import logging
+
 import epics
+from apstools.devices.aps_undulator import ApsUndulator
 
-from .instrument_registry import registry
 from .._iconfig import load_config
-from .device import await_for_connection, aload_devices, make_device
-
+from .device import aload_devices, await_for_connection, make_device
+from .instrument_registry import registry
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/xspress.py b/src/haven/instrument/xspress.py
index 3dea0ca2..136d3d84 100644
--- a/src/haven/instrument/xspress.py
+++ b/src/haven/instrument/xspress.py
@@ -1,70 +1,63 @@
-from enum import IntEnum
-from functools import partial
-import logging
 import asyncio
-from typing import Optional, Sequence, Dict
-from collections import OrderedDict
+import logging
 import time
+from collections import OrderedDict
+from enum import IntEnum
+from functools import partial
+from typing import Dict, Optional, Sequence
 
 import numpy as np
 import pandas as pd
 from apstools.devices import CamMixin_V34, SingleTrigger_V34
+from ophyd import ADComponent as ADCpt
+from ophyd import CamBase
+from ophyd import Component as Cpt
+from ophyd import DetectorBase, Device
+from ophyd import DynamicDeviceComponent as DDC
 from ophyd import (
-    ADComponent as ADCpt,
-    DetectorBase,
-    CamBase,
-    SimDetectorCam,
-    Lambda750kCam,
     EigerDetectorCam,
-    Component as Cpt,
     EpicsSignal,
     EpicsSignalRO,
     EpicsSignalWithRBV,
-    DynamicDeviceComponent as DDC,
-    SingleTrigger,
     Kind,
+    Lambda750kCam,
     OphydObject,
-    Device,
     Signal,
+    SimDetectorCam,
+    SingleTrigger,
 )
-from ophyd.status import SubscriptionStatus, StatusBase, AndStatus
-from ophyd.signal import InternalSignal, DerivedSignal
 from ophyd.areadetector.base import EpicsSignalWithRBV as SignalWithRBV
 from ophyd.areadetector.filestore_mixins import FileStoreHDF5IterativeWrite
 from ophyd.areadetector.plugins import (
-    HDF5Plugin_V34,
     HDF5Plugin_V31,
-    ImagePlugin_V34,
+    HDF5Plugin_V34,
     ImagePlugin_V31,
-    PvaPlugin_V34,
+    ImagePlugin_V34,
+    OverlayPlugin,
     PvaPlugin_V31,
-    TIFFPlugin_V34,
-    TIFFPlugin_V31,
-    ROIPlugin_V34,
+    PvaPlugin_V34,
     ROIPlugin_V31,
-    StatsPlugin_V31 as OphydStatsPlugin_V31,
-    StatsPlugin_V34 as OphydStatsPlugin_V34,
-    OverlayPlugin,
+    ROIPlugin_V34,
 )
+from ophyd.areadetector.plugins import StatsPlugin_V31 as OphydStatsPlugin_V31
+from ophyd.areadetector.plugins import StatsPlugin_V34 as OphydStatsPlugin_V34
+from ophyd.areadetector.plugins import TIFFPlugin_V31, TIFFPlugin_V34
+from ophyd.signal import DerivedSignal, InternalSignal
+from ophyd.status import AndStatus, StatusBase, SubscriptionStatus
 from pcdsdevices.signal import MultiDerivedSignal, MultiDerivedSignalRO
-from pcdsdevices.type_hints import SignalToValue, OphydDataType
+from pcdsdevices.type_hints import OphydDataType, SignalToValue
 
 from .._iconfig import load_config
-from .instrument_registry import registry
+from .device import RegexComponent as RECpt
+from .device import aload_devices, await_for_connection, make_device
 from .fluorescence_detector import (
-    XRFMixin,
-    ROIMixin,
     MCASumMixin,
-    add_roi_sums,
+    ROIMixin,
     UseROISignal,
+    XRFMixin,
+    add_roi_sums,
 )
-from .device import (
-    await_for_connection,
-    aload_devices,
-    make_device,
-    RegexComponent as RECpt,
-)
-
+from .instrument_registry import registry
 
 __all__ = ["load_xspress", "Xspress3Detector", "ROI"]
 
diff --git a/src/haven/motor_position.py b/src/haven/motor_position.py
index 84ce5550..e10cc905 100644
--- a/src/haven/motor_position.py
+++ b/src/haven/motor_position.py
@@ -1,17 +1,16 @@
-from typing import Optional, Sequence
 import logging
+import time
+from datetime import datetime
+from typing import Optional, Sequence
 
-import pymongo
-from pydantic import BaseModel
 import intake
-from bson.objectid import ObjectId
+import pymongo
 from bluesky import plan_stubs as bps
+from bson.objectid import ObjectId
+from pydantic import BaseModel
 
-from .instrument.instrument_registry import registry
 from . import exceptions
-
-import time
-from datetime import datetime
+from .instrument.instrument_registry import registry
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/plans/align_motor.py b/src/haven/plans/align_motor.py
index e7c44708..32f52dd8 100644
--- a/src/haven/plans/align_motor.py
+++ b/src/haven/plans/align_motor.py
@@ -1,11 +1,11 @@
+import logging
 import time
 import warnings
-import logging
 
-from bluesky.preprocessors import subs_decorator, subs_wrapper
-from bluesky.callbacks import best_effort
-from bluesky import plan_stubs as bps
 from apstools.plans.alignment import lineup
+from bluesky import plan_stubs as bps
+from bluesky.callbacks import best_effort
+from bluesky.preprocessors import subs_decorator, subs_wrapper
 
 from ..instrument.instrument_registry import registry
 from ..preprocessors import shutter_suspend_decorator
diff --git a/src/haven/plans/align_slits.py b/src/haven/plans/align_slits.py
index e7fd9a45..6f8bbdcb 100644
--- a/src/haven/plans/align_slits.py
+++ b/src/haven/plans/align_slits.py
@@ -6,12 +6,12 @@
 import logging
 import warnings
 
-import numpy as np
 import lmfit
-from bluesky import plans as bp, plan_stubs as bps
-from bluesky.preprocessors import subs_decorator
+import numpy as np
+from bluesky import plan_stubs as bps
+from bluesky import plans as bp
 from bluesky.callbacks import LiveFit
-
+from bluesky.preprocessors import subs_decorator
 
 __all__ = ["align_slits"]
 
diff --git a/src/haven/plans/auto_gain.py b/src/haven/plans/auto_gain.py
index 8c15ce67..85117be1 100644
--- a/src/haven/plans/auto_gain.py
+++ b/src/haven/plans/auto_gain.py
@@ -1,6 +1,7 @@
 import numpy as np
 import pandas as pd
-from bluesky import plans as bp, plan_stubs as bps
+from bluesky import plan_stubs as bps
+from bluesky import plans as bp
 from bluesky.callbacks.core import CollectThenCompute
 from bluesky.preprocessors import subs_decorator
 
diff --git a/src/haven/plans/beam_properties.py b/src/haven/plans/beam_properties.py
index de27fc00..7a126e18 100644
--- a/src/haven/plans/beam_properties.py
+++ b/src/haven/plans/beam_properties.py
@@ -3,8 +3,8 @@
 
 import matplotlib.pyplot as plt
 import numpy as np
-from lmfit.models import StepModel, GaussianModel
 from bluesky import plans as bp
+from lmfit.models import GaussianModel, StepModel
 
 from ..instrument.instrument_registry import registry
 
diff --git a/src/haven/plans/energy_scan.py b/src/haven/plans/energy_scan.py
index 4abda2b1..a10267a1 100644
--- a/src/haven/plans/energy_scan.py
+++ b/src/haven/plans/energy_scan.py
@@ -4,19 +4,18 @@
 
 import logging
 import warnings
-from typing import Sequence, Union, Mapping
 from collections import ChainMap
+from typing import Mapping, Sequence, Union
 
-from bluesky import plans as bp
 import numpy as np
+from bluesky import plans as bp
 
 from .. import merge_ranges
 from .._iconfig import load_config
-from ..instrument import registry
 from ..constants import edge_energy
-from ..typing import DetectorList
+from ..instrument import registry
 from ..preprocessors import baseline_decorator, shutter_suspend_decorator
-
+from ..typing import DetectorList
 
 __all__ = ["energy_scan"]
 
diff --git a/src/haven/plans/fly.py b/src/haven/plans/fly.py
index 1971d12a..42127e47 100644
--- a/src/haven/plans/fly.py
+++ b/src/haven/plans/fly.py
@@ -1,16 +1,18 @@
 from collections import OrderedDict, abc
 from functools import partial
-from typing import Sequence, Mapping, Union, Optional
+from typing import Mapping, Optional, Sequence, Union
 
 import numpy as np
-from bluesky import plans as bp, plan_stubs as bps, plan_patterns, preprocessors as bpp
+from bluesky import plan_patterns
+from bluesky import plan_stubs as bps
+from bluesky import plans as bp
+from bluesky import preprocessors as bpp
 from ophyd import Device
 from ophyd.flyers import FlyerInterface
 from ophyd.status import StatusBase
 
 from ..preprocessors import baseline_decorator, shutter_suspend_decorator
 
-
 __all__ = ["fly_scan", "grid_fly_scan"]
 
 
diff --git a/src/haven/plans/mono_ID_calibration.py b/src/haven/plans/mono_ID_calibration.py
index 242c3d4a..798f9f99 100644
--- a/src/haven/plans/mono_ID_calibration.py
+++ b/src/haven/plans/mono_ID_calibration.py
@@ -1,18 +1,18 @@
-from typing import Sequence, Optional
-import warnings
 import logging
+import warnings
+from typing import Optional, Sequence
 
-from bluesky.callbacks.best_effort import BestEffortCallback
-from bluesky import plan_stubs as bps
 import pandas as pd
+from bluesky import plan_stubs as bps
+from bluesky.callbacks.best_effort import BestEffortCallback
 from lmfit.model import Model
 from lmfit.models import QuadraticModel
 
 from haven.typing import Motor
+
 from ..instrument.instrument_registry import registry
+from .align_motor import align_motor, align_pitch2
 from .set_energy import set_energy
-from .align_motor import align_pitch2, align_motor
-
 
 __all__ = ["mono_ID_calibration"]
 
diff --git a/src/haven/plans/mono_gap_calibration.py b/src/haven/plans/mono_gap_calibration.py
index a29572e8..8008e04b 100644
--- a/src/haven/plans/mono_gap_calibration.py
+++ b/src/haven/plans/mono_gap_calibration.py
@@ -1,21 +1,20 @@
 import datetime as dt
-from pathlib import Path
 import logging
+from pathlib import Path
 
+from apstools.plans.alignment import lineup
+from bluesky import plan_stubs as bps
 from bluesky.callbacks import LiveFit, best_effort, fitting, mpl_plotting
 from bluesky.preprocessors import subs_decorator
-from bluesky import plan_stubs as bps
-from apstools.plans.alignment import lineup
-from matplotlib import pyplot as plt
 from lmfit.models import StepModel
+from matplotlib import pyplot as plt
 
+from .._iconfig import load_config
 from ..instrument.instrument_registry import registry
-from .set_energy import set_energy
+from .align_motor import align_pitch2
 from .auto_gain import auto_gain
 from .beam_properties import knife_scan
-from .._iconfig import load_config
-from .align_motor import align_pitch2
-
+from .set_energy import set_energy
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/plans/record_dark_current.py b/src/haven/plans/record_dark_current.py
index b34b9c6a..bf09f36d 100644
--- a/src/haven/plans/record_dark_current.py
+++ b/src/haven/plans/record_dark_current.py
@@ -1,7 +1,8 @@
-from bluesky import plans as bp, plan_stubs as bps
+from bluesky import plan_stubs as bps
+from bluesky import plans as bp
 
-from .shutters import open_shutters, close_shutters
 from ..instrument.instrument_registry import registry
+from .shutters import close_shutters, open_shutters
 
 
 def record_dark_current(ion_chambers, shutters, time):
diff --git a/src/haven/plans/set_energy.py b/src/haven/plans/set_energy.py
index fa4ff661..ae051e5c 100644
--- a/src/haven/plans/set_energy.py
+++ b/src/haven/plans/set_energy.py
@@ -1,12 +1,11 @@
-from typing import Union, Sequence
+from typing import Sequence, Union
 
 from bluesky import plan_stubs as bps
 from ophyd import Device
 
+from .. import exceptions
 from ..instrument.instrument_registry import registry
 from ..typing import DetectorList
-from .. import exceptions
-
 
 __all__ = ["set_energy"]
 
diff --git a/src/haven/plans/shutters.py b/src/haven/plans/shutters.py
index ed437b33..b71a8984 100644
--- a/src/haven/plans/shutters.py
+++ b/src/haven/plans/shutters.py
@@ -1,5 +1,6 @@
+from typing import Literal, Sequence, Union
+
 from bluesky import plan_stubs as bps
-from typing import Literal, Union, Sequence
 
 from ..instrument.instrument_registry import registry
 from ..typing import Motor
diff --git a/src/haven/plans/xafs_scan.py b/src/haven/plans/xafs_scan.py
index cf05f36b..86efd505 100644
--- a/src/haven/plans/xafs_scan.py
+++ b/src/haven/plans/xafs_scan.py
@@ -3,18 +3,18 @@
 
 """
 
-from typing import Union, Sequence, Optional, Mapping
-from collections import ChainMap
-import warnings
 import logging
+import warnings
+from collections import ChainMap
+from typing import Mapping, Optional, Sequence, Union
 
 import numpy as np
 
-from ..energy_ranges import ERange, KRange, merge_ranges
 from .. import exceptions
-from .energy_scan import energy_scan
-from ..typing import DetectorList
+from ..energy_ranges import ERange, KRange, merge_ranges
 from ..preprocessors import baseline_decorator, shutter_suspend_decorator
+from ..typing import DetectorList
+from .energy_scan import energy_scan
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/preprocessors.py b/src/haven/preprocessors.py
index ff278bdc..2acaebfb 100644
--- a/src/haven/preprocessors.py
+++ b/src/haven/preprocessors.py
@@ -1,30 +1,24 @@
 """Tools for modifying plans and data streams as they are generated."""
 
 
-from typing import Union, Sequence, Iterable
-from collections import ChainMap
-import pkg_resources
+import getpass
+import logging
 import os
 import socket
-import getpass
 import warnings
-import logging
+from collections import ChainMap
+from typing import Iterable, Sequence, Union
 
-from bluesky.preprocessors import (
-    baseline_wrapper as bluesky_baseline_wrapper,
-    finalize_wrapper,
-    suspend_wrapper,
-)
-from bluesky.suspenders import SuspendBoolLow
-from bluesky.utils import make_decorator, Msg
-from bluesky.preprocessors import msg_mutator
 import epics
+import pkg_resources
+from bluesky.preprocessors import baseline_wrapper as bluesky_baseline_wrapper
+from bluesky.preprocessors import finalize_wrapper, msg_mutator, suspend_wrapper
+from bluesky.suspenders import SuspendBoolLow
+from bluesky.utils import Msg, make_decorator
 
-
-from .instrument.instrument_registry import registry
-from .exceptions import ComponentNotFound
 from ._iconfig import load_config
-
+from .exceptions import ComponentNotFound
+from .instrument.instrument_registry import registry
 
 log = logging.getLogger()
 
diff --git a/src/haven/run_engine.py b/src/haven/run_engine.py
index 84bfb5af..bd8d266b 100644
--- a/src/haven/run_engine.py
+++ b/src/haven/run_engine.py
@@ -1,15 +1,15 @@
 import logging
 import warnings
 
-from bluesky import RunEngine as BlueskyRunEngine, suspenders
-from bluesky.callbacks.best_effort import BestEffortCallback
 import databroker
+from bluesky import RunEngine as BlueskyRunEngine
+from bluesky import suspenders
+from bluesky.callbacks.best_effort import BestEffortCallback
 
 from ._iconfig import load_config
-from .preprocessors import inject_haven_md_wrapper
 from .exceptions import ComponentNotFound
 from .instrument.instrument_registry import registry
-from .exceptions import ComponentNotFound
+from .preprocessors import inject_haven_md_wrapper
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/simulated_ioc.py b/src/haven/simulated_ioc.py
index 414b9c28..78c8b9ec 100644
--- a/src/haven/simulated_ioc.py
+++ b/src/haven/simulated_ioc.py
@@ -1,30 +1,22 @@
 #!/usr/bin/env python3
+import contextlib
+import importlib
+import logging
 import os
 import signal
-import logging
-from textwrap import dedent
-
 import time
-from subprocess import Popen, PIPE
-from typing import Optional, List, Dict, Tuple, Any
-import contextlib
-import importlib
 from pathlib import Path
+from subprocess import PIPE, Popen
+from textwrap import dedent
+from typing import Any, Dict, List, Optional, Tuple
 
-from tqdm import tqdm
 from caproto import ChannelType
-from caproto.server import (
-    PVGroup,
-    template_arg_parser,
-    pvproperty,
-    run,
-    records,
-)
+from caproto.server import PVGroup, pvproperty, records, run, template_arg_parser
 from epics import caget
+from tqdm import tqdm
 
 from . import exceptions
 
-
 log = logging.getLogger(__name__)
 
 
diff --git a/src/haven/tests/ioc_apsbss.py b/src/haven/tests/ioc_apsbss.py
index a682cc50..cbf84b77 100644
--- a/src/haven/tests/ioc_apsbss.py
+++ b/src/haven/tests/ioc_apsbss.py
@@ -2,12 +2,12 @@
 from caproto import ChannelType
 from caproto.server import (
     PVGroup,
+    PvpropertyInteger,
+    PvpropertyString,
     SubGroup,
     ioc_arg_parser,
     pvproperty,
     run,
-    PvpropertyString,
-    PvpropertyInteger,
 )
 from ophyd.tests.fake_motor_ioc import FakeMotorIOC
 
diff --git a/src/haven/tests/ioc_area_detector.py b/src/haven/tests/ioc_area_detector.py
index 1f221808..01eafc6e 100644
--- a/src/haven/tests/ioc_area_detector.py
+++ b/src/haven/tests/ioc_area_detector.py
@@ -1,13 +1,12 @@
 #!/usr/bin/env python3
 from caproto.server import (
     PVGroup,
+    PvpropertyInteger,
     SubGroup,
     ioc_arg_parser,
     pvproperty,
     run,
-    PvpropertyInteger,
 )
-
 from ophyd.tests.scaler_ioc import EpicsScalerGroup
 
 
diff --git a/src/haven/tests/ioc_dxp.py b/src/haven/tests/ioc_dxp.py
index 2d30c2e4..bb8a99ec 100755
--- a/src/haven/tests/ioc_dxp.py
+++ b/src/haven/tests/ioc_dxp.py
@@ -1,20 +1,20 @@
 #!/usr/bin/env python3
 
-from functools import partial
 import logging
+from functools import partial
 
 import numpy as np
+from caproto import ChannelType
 from caproto.server import (
     PVGroup,
+    PvpropertyDouble,
     SubGroup,
     get_pv_pair_wrapper,
     ioc_arg_parser,
     pvproperty,
-    PvpropertyDouble,
     run,
 )
-from caproto import ChannelType
-from ophyd.tests.mca_ioc import EpicsMCAGroup, EpicsDXPGroup, MCAROIGroup
+from ophyd.tests.mca_ioc import EpicsDXPGroup, EpicsMCAGroup, MCAROIGroup
 from peakutils.peak import gaussian
 
 pvproperty_with_rbv = get_pv_pair_wrapper(setpoint_suffix="", readback_suffix="_RBV")
diff --git a/src/haven/tests/ioc_preamp.py b/src/haven/tests/ioc_preamp.py
index 38662c96..0eff2300 100644
--- a/src/haven/tests/ioc_preamp.py
+++ b/src/haven/tests/ioc_preamp.py
@@ -2,12 +2,12 @@
 from caproto import ChannelType
 from caproto.server import (
     PVGroup,
+    PvpropertyInteger,
+    PvpropertyString,
     SubGroup,
     ioc_arg_parser,
     pvproperty,
     run,
-    PvpropertyString,
-    PvpropertyInteger,
 )
 from ophyd.tests.fake_motor_ioc import FakeMotorIOC
 
diff --git a/src/haven/tests/ioc_ptc10.py b/src/haven/tests/ioc_ptc10.py
index 415d1d86..60f2255a 100644
--- a/src/haven/tests/ioc_ptc10.py
+++ b/src/haven/tests/ioc_ptc10.py
@@ -1,7 +1,6 @@
 #!/usr/bin/env python3
 from caproto import ChannelType
 from caproto.server import PVGroup, SubGroup, ioc_arg_parser, pvproperty, run
-
 from ophyd.tests.scaler_ioc import EpicsScalerGroup
 
 
diff --git a/src/haven/tests/ioc_scaler.py b/src/haven/tests/ioc_scaler.py
index 12681b4e..b2d87c6d 100644
--- a/src/haven/tests/ioc_scaler.py
+++ b/src/haven/tests/ioc_scaler.py
@@ -1,7 +1,6 @@
 #!/usr/bin/env python3
 from caproto import ChannelType
 from caproto.server import PVGroup, SubGroup, ioc_arg_parser, pvproperty, run
-
 from ophyd.tests.scaler_ioc import EpicsScalerGroup
 
 
diff --git a/src/haven/tests/ioc_simple.py b/src/haven/tests/ioc_simple.py
index e1a1e667..d20b547d 100644
--- a/src/haven/tests/ioc_simple.py
+++ b/src/haven/tests/ioc_simple.py
@@ -2,11 +2,11 @@
 from caproto import ChannelType
 from caproto.server import (
     PVGroup,
+    PvpropertyDouble,
     SubGroup,
     ioc_arg_parser,
     pvproperty,
     run,
-    PvpropertyDouble,
 )
 from ophyd.tests.fake_motor_ioc import FakeMotorIOC
 
diff --git a/src/haven/tests/ioc_undulator.py b/src/haven/tests/ioc_undulator.py
index 40a012c2..5967d83b 100644
--- a/src/haven/tests/ioc_undulator.py
+++ b/src/haven/tests/ioc_undulator.py
@@ -2,11 +2,11 @@
 from caproto import ChannelType
 from caproto.server import (
     PVGroup,
+    PvpropertyDouble,
     SubGroup,
     ioc_arg_parser,
     pvproperty,
     run,
-    PvpropertyDouble,
 )
 from ophyd.tests.fake_motor_ioc import FakeMotorIOC
 
diff --git a/src/haven/tests/test_aerotech.py b/src/haven/tests/test_aerotech.py
index 081bb599..2959b2ce 100644
--- a/src/haven/tests/test_aerotech.py
+++ b/src/haven/tests/test_aerotech.py
@@ -1,12 +1,17 @@
-from unittest import mock
 from collections import OrderedDict
+from unittest import mock
 
 import numpy as np
 import pytest
 from ophyd import StatusBase
 
-from haven.instrument.aerotech import AerotechFlyer, AerotechStage, load_aerotech_stages, ureg
 from haven import exceptions
+from haven.instrument.aerotech import (
+    AerotechFlyer,
+    AerotechStage,
+    load_aerotech_stages,
+    ureg,
+)
 
 
 def test_load_aerotech_stage(sim_registry):
diff --git a/src/haven/tests/test_align_motor.py b/src/haven/tests/test_align_motor.py
index d76cb750..1a1ee550 100644
--- a/src/haven/tests/test_align_motor.py
+++ b/src/haven/tests/test_align_motor.py
@@ -1,10 +1,10 @@
 import time
-import pytest
 
 import matplotlib.pyplot as plt
+import pytest
+from bluesky import RunEngine
 from bluesky.callbacks.best_effort import BestEffortCallback
 from bluesky.callbacks.fitting import PeakStats
-from bluesky import RunEngine
 from ophyd import sim
 
 from haven import align_motor, align_pitch2, registry
diff --git a/src/haven/tests/test_beam_properties.py b/src/haven/tests/test_beam_properties.py
index 5bda7909..92f08fe0 100644
--- a/src/haven/tests/test_beam_properties.py
+++ b/src/haven/tests/test_beam_properties.py
@@ -1,7 +1,7 @@
-import pytest
-import numpy as np
 import matplotlib.pyplot as plt
-from ophyd.sim import motor1, det1, det2
+import numpy as np
+import pytest
+from ophyd.sim import det1, det2, motor1
 
 from haven.plans.beam_properties import fit_step, knife_scan
 
diff --git a/src/haven/tests/test_camera.py b/src/haven/tests/test_camera.py
index bd9821b5..0199e61b 100644
--- a/src/haven/tests/test_camera.py
+++ b/src/haven/tests/test_camera.py
@@ -1,9 +1,8 @@
 from ophyd import DetectorBase
 
-from haven import registry, load_config
+from haven import load_config, registry
 from haven.instrument.camera import AravisDetector, load_cameras
 
-
 PREFIX = "255idgigeA:"
 
 
diff --git a/src/haven/tests/test_device.py b/src/haven/tests/test_device.py
index 25352ecd..8dcb6cae 100644
--- a/src/haven/tests/test_device.py
+++ b/src/haven/tests/test_device.py
@@ -1,8 +1,8 @@
 import pytest
-from ophyd import sim, EpicsMotor
+from ophyd import EpicsMotor, sim
 
-from haven.instrument.load_instrument import load_simulated_devices
 from haven.instrument.device import make_device
+from haven.instrument.load_instrument import load_simulated_devices
 from haven.instrument.motor import HavenMotor
 
 
diff --git a/src/haven/tests/test_energy_positioner.py b/src/haven/tests/test_energy_positioner.py
index 46b2f540..29291b60 100644
--- a/src/haven/tests/test_energy_positioner.py
+++ b/src/haven/tests/test_energy_positioner.py
@@ -1,7 +1,7 @@
 import time
 
-import pytest
 import epics
+import pytest
 from ophyd.sim import instantiate_fake_device
 
 from haven.instrument.energy_positioner import EnergyPositioner
diff --git a/src/haven/tests/test_energy_ranges.py b/src/haven/tests/test_energy_ranges.py
index c42e94a7..32820934 100644
--- a/src/haven/tests/test_energy_ranges.py
+++ b/src/haven/tests/test_energy_ranges.py
@@ -1,11 +1,10 @@
-import unittest
 import logging
+import unittest
 
 import numpy as np
 
 from haven import ERange, KRange, merge_ranges
 
-
 logging.basicConfig(level=logging.INFO)
 
 
diff --git a/src/haven/tests/test_energy_xafs_scan.py b/src/haven/tests/test_energy_xafs_scan.py
index b9a73c30..bcbac538 100644
--- a/src/haven/tests/test_energy_xafs_scan.py
+++ b/src/haven/tests/test_energy_xafs_scan.py
@@ -1,10 +1,10 @@
-import pytest
 import time
 
-from ophyd import sim
 import numpy as np
+import pytest
+from ophyd import sim
 
-from haven import align_slits, energy_scan, xafs_scan, registry, KRange
+from haven import KRange, align_slits, energy_scan, registry, xafs_scan
 
 
 @pytest.fixture()
@@ -83,7 +83,7 @@ def test_energy_scan_basics(mono_motor, id_gap_motor, energies, RE):
     result = RE(scan)
     # Check that the mono and ID gap ended up in the right position
     # time.sleep(1.0)
-    assert mono_motor.readback.get() == np.max(energies) 
+    assert mono_motor.readback.get() == np.max(energies)
     # assert mono_motor.get().readback == np.max(energies)
     assert id_gap_motor.get().readback == np.max(energies)
     assert I0_exposure.get().readback == exposure_time
diff --git a/src/haven/tests/test_fluorescence_detectors.py b/src/haven/tests/test_fluorescence_detectors.py
index c7d32938..71133017 100644
--- a/src/haven/tests/test_fluorescence_detectors.py
+++ b/src/haven/tests/test_fluorescence_detectors.py
@@ -6,22 +6,22 @@
 
 """
 
+import asyncio
 import logging
+import time
 from pathlib import Path
-import asyncio
 from unittest.mock import MagicMock
-import time
 
 import numpy as np
 import pytest
-from epics import caget
-from ophyd import Kind, DynamicDeviceComponent as DDC, OphydObject, Signal
 from bluesky import plans as bp
+from epics import caget
+from ophyd import DynamicDeviceComponent as DDC
+from ophyd import Kind, OphydObject, Signal
 
-from haven.instrument.dxp import parse_xmap_buffer, load_dxp
+from haven.instrument.dxp import load_dxp, parse_xmap_buffer
 from haven.instrument.xspress import load_xspress
 
-
 DETECTORS = ["dxp", "xspress"]
 # DETECTORS = ['dxp']
 
diff --git a/src/haven/tests/test_fly_plans.py b/src/haven/tests/test_fly_plans.py
index 5a285a4e..b808d9b6 100644
--- a/src/haven/tests/test_fly_plans.py
+++ b/src/haven/tests/test_fly_plans.py
@@ -1,11 +1,11 @@
-from unittest.mock import MagicMock
 from collections import OrderedDict
+from unittest.mock import MagicMock
 
-from ophyd import sim
-from bluesky import plan_patterns
 import numpy as np
+from bluesky import plan_patterns
+from ophyd import sim
 
-from haven.plans.fly import fly_scan, grid_fly_scan, FlyerCollector
+from haven.plans.fly import FlyerCollector, fly_scan, grid_fly_scan
 
 
 def test_set_fly_params(aerotech_flyer):
diff --git a/src/haven/tests/test_heater.py b/src/haven/tests/test_heater.py
index 9edb2543..b5061a0c 100644
--- a/src/haven/tests/test_heater.py
+++ b/src/haven/tests/test_heater.py
@@ -1,8 +1,8 @@
 import time
 
 from epics import caget, caput
-from haven.instrument.heater import CapillaryHeater, load_heaters
 
+from haven.instrument.heater import CapillaryHeater, load_heaters
 
 PREFIX = "255idptc10:"
 
diff --git a/src/haven/tests/test_iconfig.py b/src/haven/tests/test_iconfig.py
index 1225acfd..840c4a82 100644
--- a/src/haven/tests/test_iconfig.py
+++ b/src/haven/tests/test_iconfig.py
@@ -1,9 +1,9 @@
+import importlib
 import os
 from pathlib import Path
-import importlib
 
 from haven import _iconfig
-from haven._iconfig import load_config, print_config_value, beamline_connected
+from haven._iconfig import beamline_connected, load_config, print_config_value
 
 
 def test_default_values():
diff --git a/src/haven/tests/test_instrument_registry.py b/src/haven/tests/test_instrument_registry.py
index 9f3f96d4..0560b937 100644
--- a/src/haven/tests/test_instrument_registry.py
+++ b/src/haven/tests/test_instrument_registry.py
@@ -1,6 +1,5 @@
 import pytest
-
-from ophyd import sim, Device, EpicsMotor
+from ophyd import Device, EpicsMotor, sim
 
 from haven.instrument import InstrumentRegistry
 from haven.instrument.instrument_registry import (
diff --git a/src/haven/tests/test_ion_chamber.py b/src/haven/tests/test_ion_chamber.py
index 5871163e..8fcc113e 100644
--- a/src/haven/tests/test_ion_chamber.py
+++ b/src/haven/tests/test_ion_chamber.py
@@ -1,12 +1,12 @@
-import pytest
 import time
 import warnings
 
+import epics
 import numpy as np
+import pytest
 
-from haven.instrument import ion_chamber
 from haven import exceptions
-import epics
+from haven.instrument import ion_chamber
 
 
 def test_gain_level(sim_ion_chamber):
diff --git a/src/haven/tests/test_lerix.py b/src/haven/tests/test_lerix.py
index 2b3e0b66..4908c7d8 100644
--- a/src/haven/tests/test_lerix.py
+++ b/src/haven/tests/test_lerix.py
@@ -1,13 +1,12 @@
-from unittest import mock
 import time
+from unittest import mock
 
-from epics import caget
 import pytest
+from epics import caget
 from ophyd.sim import instantiate_fake_device
 
-from haven.instrument import lerix
 import haven
-
+from haven.instrument import lerix
 
 um_per_mm = 1000
 
diff --git a/src/haven/tests/test_mono_ID_calibration_plan.py b/src/haven/tests/test_mono_ID_calibration_plan.py
index 5840d4b2..338b0ab7 100644
--- a/src/haven/tests/test_mono_ID_calibration_plan.py
+++ b/src/haven/tests/test_mono_ID_calibration_plan.py
@@ -1,8 +1,9 @@
 from unittest.mock import MagicMock
+
 import pytest
-from ophyd import sim
 from bluesky.callbacks.best_effort import BestEffortCallback
 from lmfit.models import QuadraticModel
+from ophyd import sim
 
 from haven import mono_ID_calibration
 
@@ -87,7 +88,9 @@ def test_aligns_mono_energy(mono_motor, id_motor, ion_chamber, pitch2_motor):
 
 
 @pytest.mark.skip(reason="``haven.plans.align_motor`` is deprecated.")
-def test_fitting_callback(mono_motor, id_motor, ion_chamber, pitch2_motor, event_loop, RE):
+def test_fitting_callback(
+    mono_motor, id_motor, ion_chamber, pitch2_motor, event_loop, RE
+):
     fit_model = MagicMock()
     plan = mono_ID_calibration(
         energies=[8000, 9000], energy_motor=mono_motor, fit_model=fit_model
diff --git a/src/haven/tests/test_plans.py b/src/haven/tests/test_plans.py
index b423c44e..294eaefa 100644
--- a/src/haven/tests/test_plans.py
+++ b/src/haven/tests/test_plans.py
@@ -8,10 +8,10 @@
 import warnings
 
 import numpy as np
-from ophyd import sim
 import pytest
+from ophyd import sim
 
-from haven import align_slits, energy_scan, xafs_scan, registry, KRange
+from haven import KRange, align_slits, energy_scan, registry, xafs_scan
 
 
 def test_align_slits(RE):
diff --git a/src/haven/tests/test_preprocessors.py b/src/haven/tests/test_preprocessors.py
index d399106c..dbce42e3 100644
--- a/src/haven/tests/test_preprocessors.py
+++ b/src/haven/tests/test_preprocessors.py
@@ -1,15 +1,15 @@
+import os
 from unittest.mock import MagicMock
 
 import pytest
 from bluesky import plans as bp
 from bluesky.callbacks import CallbackBase
 from bluesky.simulators import summarize_plan
-from ophyd.sim import det, motor, SynAxis, make_fake_device, instantiate_fake_device
-import os
+from ophyd.sim import SynAxis, det, instantiate_fake_device, make_fake_device, motor
 
-from haven import plans, baseline_decorator, baseline_wrapper, run_engine
-from haven.preprocessors import shutter_suspend_wrapper, shutter_suspend_decorator
-from haven.instrument.aps import EpicsBssDevice, load_aps, ApsMachine
+from haven import baseline_decorator, baseline_wrapper, plans, run_engine
+from haven.instrument.aps import ApsMachine, EpicsBssDevice, load_aps
+from haven.preprocessors import shutter_suspend_decorator, shutter_suspend_wrapper
 
 
 def test_shutter_suspend_wrapper(aps, shutters, sim_registry):
diff --git a/src/haven/tests/test_run_engine.py b/src/haven/tests/test_run_engine.py
index 58a02066..84fae655 100644
--- a/src/haven/tests/test_run_engine.py
+++ b/src/haven/tests/test_run_engine.py
@@ -3,8 +3,8 @@
 import databroker
 from bluesky import RunEngine
 
-from haven.instrument.aps import load_aps
 from haven import run_engine
+from haven.instrument.aps import load_aps
 
 
 def test_subscribers_garbage_collection(monkeypatch, sim_registry):
diff --git a/src/haven/tests/test_save_motor_positions.py b/src/haven/tests/test_save_motor_positions.py
index 613cfd89..5aa9663f 100644
--- a/src/haven/tests/test_save_motor_positions.py
+++ b/src/haven/tests/test_save_motor_positions.py
@@ -1,23 +1,25 @@
+import datetime as dt
 import logging
 import os
 import time
-import time_machine
-import pytz
-from zoneinfo import ZoneInfo
-import datetime as dt
 from datetime import datetime
+from zoneinfo import ZoneInfo
 
 import pytest
-from ophyd.sim import motor1, SynAxis, make_fake_device
-from ophyd import EpicsMotor, Signal, Component as Cpt
+import pytz
+import time_machine
+from ophyd import Component as Cpt
+from ophyd import EpicsMotor, Signal
+from ophyd.sim import SynAxis, make_fake_device, motor1
+
 from haven import (
-    save_motor_position,
-    registry,
+    HavenMotor,
     get_motor_position,
+    list_current_motor_positions,
     list_motor_positions,
     recall_motor_position,
-    list_current_motor_positions,
-    HavenMotor,
+    registry,
+    save_motor_position,
 )
 
 log = logging.getLogger(__name__)
diff --git a/src/haven/tests/test_scaler_triggering.py b/src/haven/tests/test_scaler_triggering.py
index d6b41bb0..dc4274c3 100644
--- a/src/haven/tests/test_scaler_triggering.py
+++ b/src/haven/tests/test_scaler_triggering.py
@@ -10,7 +10,6 @@
 import pytest
 from ophyd import Device
 
-
 from haven.instrument.scaler_triggered import ScalerTriggered
 
 
diff --git a/src/haven/tests/test_set_energy.py b/src/haven/tests/test_set_energy.py
index ccac16a3..12593406 100644
--- a/src/haven/tests/test_set_energy.py
+++ b/src/haven/tests/test_set_energy.py
@@ -1,7 +1,7 @@
 import pytest
 from ophyd.sim import motor, motor1, motor2
 
-from haven import set_energy, exceptions
+from haven import exceptions, set_energy
 
 
 def test_plan_messages():
diff --git a/src/haven/tests/test_shutter.py b/src/haven/tests/test_shutter.py
index f99888f1..2bf9d07d 100644
--- a/src/haven/tests/test_shutter.py
+++ b/src/haven/tests/test_shutter.py
@@ -1,6 +1,7 @@
 from unittest import mock
-from haven.instrument import shutter
+
 from haven import registry
+from haven.instrument import shutter
 
 
 def test_shutter(sim_registry, beamline_connected):
diff --git a/src/haven/tests/test_stages.py b/src/haven/tests/test_stages.py
index 401fd68c..663ae64e 100644
--- a/src/haven/tests/test_stages.py
+++ b/src/haven/tests/test_stages.py
@@ -2,14 +2,15 @@
 
 
 import time
-from unittest import mock
 from collections import OrderedDict
+from datetime import datetime
+from unittest import mock
+
+import numpy as np
 import pytest
 from ophyd.sim import instantiate_fake_device, make_fake_device
-import numpy as np
-from datetime import datetime
 
-from haven import registry, exceptions
+from haven import exceptions, registry
 from haven.instrument import stage
 
 
diff --git a/src/haven/tests/test_xdi_writer.py b/src/haven/tests/test_xdi_writer.py
index 900dfba7..2a8ea245 100644
--- a/src/haven/tests/test_xdi_writer.py
+++ b/src/haven/tests/test_xdi_writer.py
@@ -1,23 +1,22 @@
-import pytest
-from unittest import TestCase, expectedFailure
-from io import StringIO
+import datetime as dt
+import logging
 import os
 import time
+from io import StringIO
 from pathlib import Path
-import datetime as dt
-import logging
+from unittest import TestCase, expectedFailure
+
+import numpy as np
+import pytest
 
 # from freezegun import freeze_time
 import pytz
 import time_machine
 from bluesky import RunEngine
-from ophyd.sim import motor, SynGauss, SynAxis
-import numpy as np
 from numpy import asarray as array
+from ophyd.sim import SynAxis, SynGauss, motor
 
-
-from haven import XDIWriter, exceptions, energy_scan
-
+from haven import XDIWriter, energy_scan, exceptions
 
 fake_time = pytz.timezone("America/New_York").localize(
     dt.datetime(2022, 8, 19, 19, 10, 51)
@@ -51,23 +50,31 @@
     "num_points": 10,
     "plan_args": {
         "args": [
-            "SynAxis(prefix='', name='energy', "
-            "read_attrs=['readback', 'setpoint'], "
-            "configuration_attrs=['velocity', 'acceleration'])",
+            (
+                "SynAxis(prefix='', name='energy', "
+                "read_attrs=['readback', 'setpoint'], "
+                "configuration_attrs=['velocity', 'acceleration'])"
+            ),
             array([8300, 8310, 8320, 8330, 8340, 8350, 8360, 8370, 8380, 8390]),
-            "SynAxis(prefix='', name='exposure', "
-            "read_attrs=['readback', 'setpoint'], "
-            "configuration_attrs=['velocity', 'acceleration'])",
+            (
+                "SynAxis(prefix='', name='exposure', "
+                "read_attrs=['readback', 'setpoint'], "
+                "configuration_attrs=['velocity', 'acceleration'])"
+            ),
             [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
         ],
         "detectors": [
-            "SynGauss(prefix='', name='I0', "
-            "read_attrs=['val'], configuration_attrs=['Imax', "
-            "'center', 'sigma', 'noise', 'noise_multiplier'])",
-            "SynGauss(prefix='', name='It', "
-            "read_attrs=['val'], configuration_attrs=['Imax', "
-            "'center', 'sigma', 'noise', "
-            "'noise_multiplier'])",
+            (
+                "SynGauss(prefix='', name='I0', "
+                "read_attrs=['val'], configuration_attrs=['Imax', "
+                "'center', 'sigma', 'noise', 'noise_multiplier'])"
+            ),
+            (
+                "SynGauss(prefix='', name='It', "
+                "read_attrs=['val'], configuration_attrs=['Imax', "
+                "'center', 'sigma', 'noise', "
+                "'noise_multiplier'])"
+            ),
         ],
         "per_step": "None",
     },
@@ -75,15 +82,19 @@
     "plan_pattern": "inner_list_product",
     "plan_pattern_args": {
         "args": [
-            "SynAxis(prefix='', name='energy', "
-            "read_attrs=['readback', 'setpoint'], "
-            "configuration_attrs=['velocity', "
-            "'acceleration'])",
+            (
+                "SynAxis(prefix='', name='energy', "
+                "read_attrs=['readback', 'setpoint'], "
+                "configuration_attrs=['velocity', "
+                "'acceleration'])"
+            ),
             array([8300, 8310, 8320, 8330, 8340, 8350, 8360, 8370, 8380, 8390]),
-            "SynAxis(prefix='', name='exposure', "
-            "read_attrs=['readback', 'setpoint'], "
-            "configuration_attrs=['velocity', "
-            "'acceleration'])",
+            (
+                "SynAxis(prefix='', name='exposure', "
+                "read_attrs=['readback', 'setpoint'], "
+                "configuration_attrs=['velocity', "
+                "'acceleration'])"
+            ),
             [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1],
         ]
     },
diff --git a/src/haven/tests/test_xray_source.py b/src/haven/tests/test_xray_source.py
index 8a1c84b0..17404e23 100644
--- a/src/haven/tests/test_xray_source.py
+++ b/src/haven/tests/test_xray_source.py
@@ -1,7 +1,7 @@
 from unittest import mock
 
-from haven.instrument.xray_source import load_xray_sources
 import haven
+from haven.instrument.xray_source import load_xray_sources
 
 
 def test_load_xray_sources(sim_registry, beamline_connected):
diff --git a/src/haven/tests/test_xspress.py b/src/haven/tests/test_xspress.py
index 3c4b2a53..72e22a30 100644
--- a/src/haven/tests/test_xspress.py
+++ b/src/haven/tests/test_xspress.py
@@ -1,6 +1,7 @@
 import pytest
-from ophyd.sim import make_fake_device
 from ophyd.device import do_not_wait_for_lazy_connection
+from ophyd.sim import make_fake_device
+
 from haven.instrument.xspress import Xspress3Detector
 
 
diff --git a/src/haven/typing.py b/src/haven/typing.py
index 7e75e57a..d4c7c1d0 100644
--- a/src/haven/typing.py
+++ b/src/haven/typing.py
@@ -1,7 +1,6 @@
-from typing import Union, Sequence
-
-from ophyd import Device, Component, Signal
+from typing import Sequence, Union
 
+from ophyd import Component, Device, Signal
 
 Detector = Union[Device, Component, Signal, str]
 
diff --git a/src/haven/xdi_writer.py b/src/haven/xdi_writer.py
index eccbf64b..16f5deed 100644
--- a/src/haven/xdi_writer.py
+++ b/src/haven/xdi_writer.py
@@ -1,16 +1,15 @@
+import datetime as dt
 import logging
-import warnings
-import unicodedata
 import re
-import datetime as dt
+import unicodedata
+import warnings
 from pathlib import Path
-from typing import Sequence, Optional, Union
+from typing import Optional, Sequence, Union
 
 from bluesky.callbacks import CallbackBase
 
 from . import exceptions
 
-
 log = logging.getLogger(__name__)
 
 

From e6dc74df1f0a5b44c7ba0da500cd8ece48e33d4f Mon Sep 17 00:00:00 2001
From: Mark Wolfman <canismarko@gmail.com>
Date: Wed, 15 Nov 2023 22:58:35 -0600
Subject: [PATCH 7/9] Removed unused imports.

---
 ipython_startup.py                            | 12 +++---
 src/conftest.py                               |  3 --
 src/firefly/__init__.py                       |  4 +-
 src/firefly/application.py                    |  9 +----
 src/firefly/area_detector_viewer.py           |  2 -
 src/firefly/bss.py                            |  2 +-
 src/firefly/camera.py                         |  4 --
 src/firefly/ion_chamber.py                    |  3 --
 src/firefly/launcher.py                       |  2 -
 src/firefly/main_window.py                    |  1 -
 src/firefly/motor.py                          |  2 -
 src/firefly/plans/count.py                    |  1 -
 src/firefly/queue_client.py                   |  2 +-
 src/firefly/run_browser.py                    | 12 ++----
 src/firefly/status.py                         |  1 -
 src/firefly/tests/test_application.py         | 10 -----
 .../tests/test_area_detector_display.py       |  1 -
 src/firefly/tests/test_bss_display.py         |  3 --
 src/firefly/tests/test_cameras_display.py     |  2 -
 src/firefly/tests/test_energy_display.py      |  6 +--
 src/firefly/tests/test_main_window.py         |  6 ---
 src/firefly/tests/test_motor_menu.py          | 12 +-----
 src/firefly/tests/test_ophyd_connection.py    |  5 +--
 src/firefly/tests/test_queue_client.py        |  9 -----
 src/firefly/tests/test_run_browser.py         |  6 ---
 src/firefly/tests/test_tiled_server.py        |  2 -
 src/firefly/tests/test_voltmeters.py          |  5 ---
 src/firefly/tests/test_xafs_scan.py           |  2 -
 .../tests/test_xrf_detector_display.py        |  5 +--
 src/firefly/voltmeter.py                      |  3 +-
 src/firefly/voltmeters.py                     |  1 -
 src/firefly/xrf_detector.py                   |  8 +---
 src/firefly/xrf_roi.py                        |  1 -
 src/haven/__init__.py                         |  2 +-
 src/haven/_iconfig.py                         |  1 -
 src/haven/exceptions.py                       |  2 +-
 src/haven/instrument/aerotech.py              | 10 ++---
 src/haven/instrument/aps.py                   |  3 --
 src/haven/instrument/area_detector.py         |  5 +--
 src/haven/instrument/camera.py                | 17 ++-------
 src/haven/instrument/dxp.py                   | 18 +--------
 src/haven/instrument/fluorescence_detector.py | 20 +---------
 src/haven/instrument/heater.py                |  3 +-
 src/haven/instrument/instrument_registry.py   |  9 -----
 src/haven/instrument/ion_chamber.py           | 17 ++-------
 src/haven/instrument/labjack.py               |  3 +-
 src/haven/instrument/lerix.py                 |  3 +-
 src/haven/instrument/monochromator.py         |  4 +-
 src/haven/instrument/motor.py                 | 10 +----
 src/haven/instrument/power_supply.py          |  3 +-
 src/haven/instrument/scaler_triggered.py      |  2 +-
 src/haven/instrument/shutter.py               |  6 +--
 src/haven/instrument/stage.py                 | 20 ++--------
 src/haven/instrument/xray_source.py           |  4 +-
 src/haven/instrument/xspress.py               | 38 +++----------------
 src/haven/plans/align_motor.py                |  2 +-
 src/haven/plans/energy_scan.py                |  4 +-
 src/haven/plans/fly.py                        |  5 +--
 src/haven/plans/mono_ID_calibration.py        |  1 -
 src/haven/plans/mono_gap_calibration.py       |  2 -
 src/haven/plans/record_dark_current.py        |  1 -
 src/haven/plans/set_energy.py                 |  3 +-
 src/haven/plans/xafs_scan.py                  |  4 +-
 src/haven/preprocessors.py                    |  2 +-
 src/haven/run_engine.py                       | 22 -----------
 src/haven/tests/ioc_apsbss.py                 |  5 ---
 src/haven/tests/ioc_area_detector.py          |  1 -
 src/haven/tests/ioc_dxp.py                    |  1 -
 src/haven/tests/ioc_mono.py                   |  4 +-
 src/haven/tests/ioc_motor.py                  |  3 +-
 src/haven/tests/ioc_preamp.py                 | 13 +------
 src/haven/tests/ioc_ptc10.py                  |  2 -
 src/haven/tests/ioc_scaler.py                 |  1 -
 src/haven/tests/ioc_simple.py                 | 13 +------
 src/haven/tests/ioc_undulator.py              | 11 +-----
 src/haven/tests/test_align_motor.py           |  6 +--
 src/haven/tests/test_aps.py                   |  6 +--
 src/haven/tests/test_area_detector.py         |  2 -
 src/haven/tests/test_beam_properties.py       |  1 -
 src/haven/tests/test_energy_positioner.py     |  1 -
 src/haven/tests/test_energy_xafs_scan.py      |  4 +-
 .../tests/test_fluorescence_detectors.py      |  7 +---
 src/haven/tests/test_fly_plans.py             |  1 -
 src/haven/tests/test_heater.py                |  6 +--
 src/haven/tests/test_instrument_registry.py   |  2 +-
 src/haven/tests/test_ion_chamber.py           |  3 --
 src/haven/tests/test_lerix.py                 |  3 --
 .../tests/test_mono_ID_calibration_plan.py    |  2 -
 src/haven/tests/test_monochromator.py         |  4 --
 src/haven/tests/test_motor.py                 |  2 -
 src/haven/tests/test_plans.py                 |  4 +-
 src/haven/tests/test_preprocessors.py         |  9 ++---
 src/haven/tests/test_save_motor_positions.py  |  8 +---
 src/haven/tests/test_shutter.py               |  2 -
 src/haven/tests/test_stages.py                |  7 ----
 src/haven/tests/test_xray_source.py           |  3 --
 src/haven/tests/test_xspress.py               |  2 -
 tests/test_simulated_ioc.py                   |  3 --
 98 files changed, 84 insertions(+), 451 deletions(-)

diff --git a/ipython_startup.py b/ipython_startup.py
index 1781e178..71bd9936 100644
--- a/ipython_startup.py
+++ b/ipython_startup.py
@@ -1,10 +1,10 @@
-from bluesky import plans as bp, plan_stubs as bps, RunEngine, suspenders
-from bluesky.simulators import summarize_plan
-from bluesky.callbacks.best_effort import BestEffortCallback
-import databroker
-import matplotlib.pyplot as plt
+from bluesky import plans as bp, plan_stubs as bps, RunEngine, suspenders  # noqa: F401
+from bluesky.simulators import summarize_plan  # noqa: F401
+from bluesky.callbacks.best_effort import BestEffortCallback  # noqa: F401
+import databroker  # noqa: F401
+import matplotlib.pyplot as plt  # noqa: F401
 
-import haven
+import haven  # noqa: F401
 
 # Prepare the haven instrument
 config = haven.load_config()
diff --git a/src/conftest.py b/src/conftest.py
index ef3bd3c8..7702039a 100644
--- a/src/conftest.py
+++ b/src/conftest.py
@@ -116,10 +116,7 @@ def kill_process(process_name):
 def sim_registry(monkeypatch):
     # mock out Ophyd connections so devices can be created
     modules = [
-        haven.instrument.fluorescence_detector,
-        haven.instrument.monochromator,
         haven.instrument.ion_chamber,
-        haven.instrument.motor,
         haven.instrument.device,
     ]
     for mod in modules:
diff --git a/src/firefly/__init__.py b/src/firefly/__init__.py
index ed977ab9..f3ad9a95 100644
--- a/src/firefly/__init__.py
+++ b/src/firefly/__init__.py
@@ -1,2 +1,2 @@
-from . import display
-from .application import FireflyApplication
+from . import display  # noqa: F401
+from .application import FireflyApplication  # noqa: F401
diff --git a/src/firefly/application.py b/src/firefly/application.py
index 5cea055c..0d95eded 100644
--- a/src/firefly/application.py
+++ b/src/firefly/application.py
@@ -1,25 +1,20 @@
 import logging
 import subprocess
 from collections import OrderedDict
-from dataclasses import dataclass, field
 from functools import partial
 from pathlib import Path
-from typing import Mapping, Optional, Sequence, Union
+from typing import Mapping, Sequence
 
 import pydm
 import pyqtgraph as pg
 import qtawesome as qta
-from bluesky_queueserver_api import BPlan
-from bluesky_queueserver_api.zmq import REManagerAPI
 from pydm.application import PyDMApplication
-from pydm.display import load_file
 from pydm.utilities.stylesheet import apply_stylesheet
 from PyQt5.QtWidgets import QStyleFactory
 from qtpy import QtCore, QtWidgets
-from qtpy.QtCore import QObject, QThread, Signal, Slot
+from qtpy.QtCore import Signal
 from qtpy.QtWidgets import QAction
 
-import haven
 from haven import HavenMotor, load_config, registry
 from haven.exceptions import ComponentNotFound
 
diff --git a/src/firefly/area_detector_viewer.py b/src/firefly/area_detector_viewer.py
index c180cf51..10ff75c4 100644
--- a/src/firefly/area_detector_viewer.py
+++ b/src/firefly/area_detector_viewer.py
@@ -1,8 +1,6 @@
 import logging
-import subprocess
 import sys
 
-import matplotlib.pyplot as plt
 import numpy as np
 import pydm
 import pyqtgraph
diff --git a/src/firefly/bss.py b/src/firefly/bss.py
index 2bab7ca0..380e0a0c 100644
--- a/src/firefly/bss.py
+++ b/src/firefly/bss.py
@@ -3,7 +3,7 @@
 
 import qtawesome as qta
 from apsbss import apsbss
-from qtpy.QtCore import Signal, Slot
+from qtpy.QtCore import Signal
 from qtpy.QtGui import QStandardItem, QStandardItemModel
 
 import haven
diff --git a/src/firefly/camera.py b/src/firefly/camera.py
index a5a0330a..2c2ed4f3 100644
--- a/src/firefly/camera.py
+++ b/src/firefly/camera.py
@@ -1,7 +1,4 @@
-import datetime as dt
 import logging
-import os
-import subprocess
 from enum import IntEnum
 from pathlib import Path
 
@@ -10,7 +7,6 @@
 from qtpy.QtCore import Slot
 from qtpy.QtGui import QColor
 
-import haven
 from firefly import FireflyApplication, display
 
 log = logging.getLogger(__name__)
diff --git a/src/firefly/ion_chamber.py b/src/firefly/ion_chamber.py
index 99707dca..16347572 100644
--- a/src/firefly/ion_chamber.py
+++ b/src/firefly/ion_chamber.py
@@ -1,6 +1,3 @@
-import warnings
-
-import haven
 from firefly import display
 
 
diff --git a/src/firefly/launcher.py b/src/firefly/launcher.py
index faaa91e2..b026ad6f 100644
--- a/src/firefly/launcher.py
+++ b/src/firefly/launcher.py
@@ -24,7 +24,6 @@ def main(default_fullscreen=False, default_display="status"):
         otherwise we get the following error if someone adds a WebView at Designer:
         ImportError: QtWebEngineWidgets must be imported before a QCoreApplication instance is created
         """
-        from qtpy import QtWebEngineWidgets
     except ImportError:
         logger.debug("QtWebEngine is not supported.")
 
@@ -57,7 +56,6 @@ def main(default_fullscreen=False, default_display="status"):
 
     setup_renderer()
 
-    import pydm
     from pydm.utilities.macro import parse_macro_string
 
     parser = argparse.ArgumentParser(description="Python Display Manager")
diff --git a/src/firefly/main_window.py b/src/firefly/main_window.py
index a39299b1..f7bf69cc 100644
--- a/src/firefly/main_window.py
+++ b/src/firefly/main_window.py
@@ -9,7 +9,6 @@
 from qtpy import QtCore, QtGui, QtWidgets
 
 from haven import load_config
-from haven.instrument import motor
 
 log = logging.getLogger(__name__)
 
diff --git a/src/firefly/motor.py b/src/firefly/motor.py
index 292afbea..bace9cac 100644
--- a/src/firefly/motor.py
+++ b/src/firefly/motor.py
@@ -1,5 +1,3 @@
-import warnings
-
 import haven
 from firefly import display
 
diff --git a/src/firefly/plans/count.py b/src/firefly/plans/count.py
index 77373ab2..4c06281c 100644
--- a/src/firefly/plans/count.py
+++ b/src/firefly/plans/count.py
@@ -1,7 +1,6 @@
 import logging
 
 from bluesky_queueserver_api import BPlan
-from qtpy import QtWidgets
 
 from firefly import display
 
diff --git a/src/firefly/queue_client.py b/src/firefly/queue_client.py
index 6df5f5e7..d498392c 100644
--- a/src/firefly/queue_client.py
+++ b/src/firefly/queue_client.py
@@ -3,7 +3,7 @@
 import warnings
 from typing import Optional
 
-from bluesky_queueserver_api import BPlan, comm_base
+from bluesky_queueserver_api import comm_base
 from bluesky_queueserver_api.zmq import REManagerAPI
 from qtpy.QtCore import QObject, QThread, QTimer, Signal, Slot
 from qtpy.QtWidgets import QAction
diff --git a/src/firefly/run_browser.py b/src/firefly/run_browser.py
index 4f112a42..b618a0cb 100644
--- a/src/firefly/run_browser.py
+++ b/src/firefly/run_browser.py
@@ -1,24 +1,20 @@
-import datetime as dt
 import logging
-import warnings
-from contextlib import contextmanager
 from itertools import count
 from typing import Sequence
 
 import numpy as np
 import qtawesome as qta
 import yaml
-from httpx import HTTPStatusError, PoolTimeout
 from matplotlib.colors import TABLEAU_COLORS
 from pydantic.error_wrappers import ValidationError
-from pyqtgraph import GraphicsLayoutWidget, PlotDataItem, PlotItem, PlotWidget
-from qtpy.QtCore import Qt, QThread, Signal, Slot
+from pyqtgraph import PlotItem, PlotWidget
+from qtpy.QtCore import Qt, QThread, Signal
 from qtpy.QtGui import QStandardItem, QStandardItemModel
 from qtpy.QtWidgets import QWidget
 
-from firefly import FireflyApplication, display
+from haven import exceptions
+from firefly import display
 from firefly.run_client import DatabaseWorker
-from haven import exceptions, load_config, tiled_client
 
 log = logging.getLogger(__name__)
 
diff --git a/src/firefly/status.py b/src/firefly/status.py
index 2e830b3f..27be9b7d 100644
--- a/src/firefly/status.py
+++ b/src/firefly/status.py
@@ -1,6 +1,5 @@
 import logging
 
-import haven
 from firefly import FireflyApplication, display
 
 log = logging.getLogger(__name__)
diff --git a/src/firefly/tests/test_application.py b/src/firefly/tests/test_application.py
index 5f10cefa..839f83f8 100644
--- a/src/firefly/tests/test_application.py
+++ b/src/firefly/tests/test_application.py
@@ -1,16 +1,6 @@
-import asyncio
-import time
 from unittest.mock import MagicMock
 
 import pytest
-from bluesky import RunEngine
-from bluesky import plans as bp
-from bluesky_queueserver_api.zmq import REManagerAPI
-from qtpy.QtCore import QThread
-from qtpy.QtTest import QSignalSpy
-
-from firefly.application import REManagerAPI
-from firefly.queue_client import QueueClient
 
 
 def test_setup(ffapp):
diff --git a/src/firefly/tests/test_area_detector_display.py b/src/firefly/tests/test_area_detector_display.py
index c630bfd2..8fdc4a8d 100644
--- a/src/firefly/tests/test_area_detector_display.py
+++ b/src/firefly/tests/test_area_detector_display.py
@@ -5,7 +5,6 @@
 import pyqtgraph
 
 from firefly.area_detector_viewer import AreaDetectorViewerDisplay
-from haven.instrument.camera import load_cameras
 
 
 def test_open_camera_viewer_actions(ffapp, qtbot, sim_camera):
diff --git a/src/firefly/tests/test_bss_display.py b/src/firefly/tests/test_bss_display.py
index a78ee358..ba2dbeed 100644
--- a/src/firefly/tests/test_bss_display.py
+++ b/src/firefly/tests/test_bss_display.py
@@ -1,7 +1,4 @@
-import time
-
 import pytest
-from epics import caget, caput
 from qtpy.QtCore import Qt
 from qtpy.QtGui import QStandardItemModel
 
diff --git a/src/firefly/tests/test_cameras_display.py b/src/firefly/tests/test_cameras_display.py
index 234bd4ec..168325e5 100644
--- a/src/firefly/tests/test_cameras_display.py
+++ b/src/firefly/tests/test_cameras_display.py
@@ -1,11 +1,9 @@
 import json
 
 import pytest
-from pydm.data_plugins.epics_plugin import EPICSPlugin
 from pydm.widgets.channel import PyDMChannel
 from qtpy import QtCore, QtGui, QtWidgets
 
-import haven
 from firefly.camera import CameraDisplay, DetectorStates
 from firefly.cameras import CamerasDisplay
 
diff --git a/src/firefly/tests/test_energy_display.py b/src/firefly/tests/test_energy_display.py
index 5ac70962..50b9f3fe 100644
--- a/src/firefly/tests/test_energy_display.py
+++ b/src/firefly/tests/test_energy_display.py
@@ -1,16 +1,12 @@
-import time
 from unittest import mock
 
-import pytest
 from apstools.devices.aps_undulator import ApsUndulator
 from bluesky_queueserver_api import BPlan
 from ophyd.sim import make_fake_device
-from qtpy import QtCore, QtWidgets
+from qtpy import QtCore
 
 import haven
 from firefly.energy import EnergyDisplay
-from haven.instrument.energy_positioner import load_energy_positioner
-from haven.instrument.monochromator import load_monochromator
 
 FakeMonochromator = make_fake_device(haven.instrument.monochromator.Monochromator)
 FakeEnergyPositioner = make_fake_device(
diff --git a/src/firefly/tests/test_main_window.py b/src/firefly/tests/test_main_window.py
index 39e95043..19d54113 100644
--- a/src/firefly/tests/test_main_window.py
+++ b/src/firefly/tests/test_main_window.py
@@ -1,9 +1,3 @@
-import time
-from unittest import mock
-
-import pytest
-
-from firefly.application import FireflyApplication
 from firefly.main_window import FireflyMainWindow, PlanMainWindow
 
 
diff --git a/src/firefly/tests/test_motor_menu.py b/src/firefly/tests/test_motor_menu.py
index aa623203..8ae220d0 100644
--- a/src/firefly/tests/test_motor_menu.py
+++ b/src/firefly/tests/test_motor_menu.py
@@ -1,16 +1,8 @@
-import asyncio
-import logging
-import time
-from unittest import mock
-
-import epics
 import pytest
-from ophyd.sim import instantiate_fake_device, make_fake_device
-from qtpy import QtWidgets
+from ophyd.sim import make_fake_device
 
-from firefly.application import FireflyApplication
 from firefly.main_window import FireflyMainWindow
-from haven.instrument import motor, registry
+from haven.instrument import motor
 
 
 @pytest.fixture
diff --git a/src/firefly/tests/test_ophyd_connection.py b/src/firefly/tests/test_ophyd_connection.py
index 1d5b59f4..d6e3210b 100644
--- a/src/firefly/tests/test_ophyd_connection.py
+++ b/src/firefly/tests/test_ophyd_connection.py
@@ -2,10 +2,9 @@
 from unittest.mock import MagicMock
 
 import pytest
-from ophyd import EpicsMotor, EpicsSignal, sim
-from ophyd.sim import instantiate_fake_device, make_fake_device
+from ophyd.sim import make_fake_device
 from pydm import PyDMChannel
-from pydm.data_plugins import add_plugin, plugin_for_address
+from pydm.data_plugins import plugin_for_address
 from pydm.main_window import PyDMMainWindow
 from pydm.widgets import PyDMLineEdit
 from qtpy import QtCore
diff --git a/src/firefly/tests/test_queue_client.py b/src/firefly/tests/test_queue_client.py
index a08153af..82597035 100644
--- a/src/firefly/tests/test_queue_client.py
+++ b/src/firefly/tests/test_queue_client.py
@@ -1,19 +1,10 @@
-import asyncio
-import time
-from collections import ChainMap
 from unittest.mock import MagicMock
 
 import pytest
-from bluesky import RunEngine
-from bluesky import plans as bp
 from bluesky_queueserver_api import BPlan
-from bluesky_queueserver_api.zmq import REManagerAPI
 from pytestqt.exceptions import TimeoutError
-from qtpy.QtCore import QThread
-from qtpy.QtTest import QSignalSpy
 from qtpy.QtWidgets import QAction
 
-from firefly.application import REManagerAPI
 from firefly.queue_client import QueueClient
 
 qs_status = {
diff --git a/src/firefly/tests/test_run_browser.py b/src/firefly/tests/test_run_browser.py
index 9a3b8d4e..48108bbc 100644
--- a/src/firefly/tests/test_run_browser.py
+++ b/src/firefly/tests/test_run_browser.py
@@ -1,6 +1,4 @@
 import logging
-import time
-from collections import namedtuple
 from unittest.mock import MagicMock
 
 import numpy as np
@@ -8,16 +6,13 @@
 import pytest
 from pyqtgraph import PlotItem, PlotWidget
 from qtpy.QtCore import Qt
-from tiled.adapters.array import ArrayAdapter
 from tiled.adapters.mapping import MapAdapter
 from tiled.adapters.xarray import DatasetAdapter
 from tiled.client import Context, from_context
 from tiled.server.app import build_app
 
-from firefly.main_window import PlanMainWindow
 from firefly.run_browser import RunBrowserDisplay
 from firefly.run_client import DatabaseWorker
-from haven import tiled_client
 
 log = logging.getLogger(__name__)
 
@@ -108,7 +103,6 @@ def display(ffapp, client, qtbot):
 
 def test_client_fixture(client):
     """Does the client fixture load without stalling the test runner?"""
-    pass
 
 
 def test_run_viewer_action(ffapp, monkeypatch):
diff --git a/src/firefly/tests/test_tiled_server.py b/src/firefly/tests/test_tiled_server.py
index 504b23d9..50f21180 100644
--- a/src/firefly/tests/test_tiled_server.py
+++ b/src/firefly/tests/test_tiled_server.py
@@ -4,7 +4,6 @@
 import pytest
 from tiled.adapters.array import ArrayAdapter
 from tiled.adapters.mapping import MapAdapter
-from tiled.adapters.xarray import DatasetAdapter
 from tiled.client import Context, from_context
 from tiled.server.app import build_app
 
@@ -33,4 +32,3 @@ def client():
 
 def test_client_fixture(client):
     """Does the client fixture load without stalling the test runner?"""
-    pass
diff --git a/src/firefly/tests/test_voltmeters.py b/src/firefly/tests/test_voltmeters.py
index 468ff0f7..dff799ee 100644
--- a/src/firefly/tests/test_voltmeters.py
+++ b/src/firefly/tests/test_voltmeters.py
@@ -1,11 +1,6 @@
-import time
-from unittest import mock
-
 import pytest
-from qtpy import QtWidgets
 
 import haven
-from firefly.application import FireflyApplication
 from firefly.main_window import FireflyMainWindow
 from firefly.voltmeter import VoltmeterDisplay
 from firefly.voltmeters import VoltmetersDisplay
diff --git a/src/firefly/tests/test_xafs_scan.py b/src/firefly/tests/test_xafs_scan.py
index 8ea85762..95df5cb7 100644
--- a/src/firefly/tests/test_xafs_scan.py
+++ b/src/firefly/tests/test_xafs_scan.py
@@ -1,5 +1,3 @@
-import pytest
-
 from firefly.xafs_scan import XafsScanDisplay
 
 
diff --git a/src/firefly/tests/test_xrf_detector_display.py b/src/firefly/tests/test_xrf_detector_display.py
index 0b63b68f..aee745ed 100644
--- a/src/firefly/tests/test_xrf_detector_display.py
+++ b/src/firefly/tests/test_xrf_detector_display.py
@@ -1,12 +1,9 @@
-import time
-
 import numpy as np
 import pytest
-from ophyd import Kind
 from pyqtgraph import PlotItem
 from qtpy import QtCore
 
-from firefly.xrf_detector import XRFDetectorDisplay, XRFPlotWidget
+from firefly.xrf_detector import XRFDetectorDisplay
 from firefly.xrf_roi import XRFROIDisplay
 
 detectors = ["dxp", "xspress"]
diff --git a/src/firefly/voltmeter.py b/src/firefly/voltmeter.py
index 5dfea5da..c9104e92 100644
--- a/src/firefly/voltmeter.py
+++ b/src/firefly/voltmeter.py
@@ -2,10 +2,9 @@
 from typing import Mapping, Optional, Sequence
 
 import qtawesome as qta
-from pydm.widgets.channel import PyDMChannel
 
 from firefly import FireflyApplication, display
-from haven import exceptions, load_config, registry
+from haven import exceptions, registry
 from haven.instrument.ion_chamber import IonChamber
 
 log = logging.getLogger(__name__)
diff --git a/src/firefly/voltmeters.py b/src/firefly/voltmeters.py
index 0ec7be8c..76c46cf9 100644
--- a/src/firefly/voltmeters.py
+++ b/src/firefly/voltmeters.py
@@ -1,6 +1,5 @@
 import json
 import logging
-import warnings
 from typing import Mapping, Optional, Sequence
 
 import qtawesome as qta
diff --git a/src/firefly/xrf_detector.py b/src/firefly/xrf_detector.py
index f2c1bc27..fedfe43c 100644
--- a/src/firefly/xrf_detector.py
+++ b/src/firefly/xrf_detector.py
@@ -1,17 +1,13 @@
-import gc
 import json
 import logging
-import subprocess
 import sys
-import time
 from collections import defaultdict
 from contextlib import contextmanager
 from enum import IntEnum
 from functools import partial
 from pathlib import Path
-from typing import Optional, Sequence
+from typing import Sequence
 
-import matplotlib.pyplot as plt
 import numpy as np
 import pydm
 import pyqtgraph
@@ -19,7 +15,7 @@
 from matplotlib.colors import TABLEAU_COLORS
 from pydm.widgets import PyDMChannel, PyDMEmbeddedDisplay
 from qtpy import uic
-from qtpy.QtCore import QObject, Qt, QThread, Signal
+from qtpy.QtCore import Qt, Signal
 from qtpy.QtWidgets import QWidget
 
 import haven
diff --git a/src/firefly/xrf_roi.py b/src/firefly/xrf_roi.py
index 69621027..8fa808f7 100644
--- a/src/firefly/xrf_roi.py
+++ b/src/firefly/xrf_roi.py
@@ -1,5 +1,4 @@
 import qtawesome as qta
-from pydm import PyDMChannel
 from qtpy.QtCore import Signal
 
 from firefly import display
diff --git a/src/haven/__init__.py b/src/haven/__init__.py
index fb1469f5..be245e09 100644
--- a/src/haven/__init__.py
+++ b/src/haven/__init__.py
@@ -6,7 +6,7 @@
 
 #  Top-level imports
 from .catalog import load_catalog, load_data, load_result, tiled_client  # noqa: F401
-from .constants import edge_energy
+from .constants import edge_energy  # noqa: F401
 from .energy_ranges import ERange, KRange, merge_ranges  # noqa: F401
 from .instrument import (  # noqa: F401
     InstrumentRegistry,
diff --git a/src/haven/_iconfig.py b/src/haven/_iconfig.py
index 529684e9..ec5fa75c 100644
--- a/src/haven/_iconfig.py
+++ b/src/haven/_iconfig.py
@@ -15,7 +15,6 @@
 import pathlib
 from contextlib import contextmanager
 from copy import deepcopy
-from functools import lru_cache
 from typing import Sequence
 
 import tomli
diff --git a/src/haven/exceptions.py b/src/haven/exceptions.py
index c4652591..e53b324c 100644
--- a/src/haven/exceptions.py
+++ b/src/haven/exceptions.py
@@ -1,4 +1,4 @@
-from .instrument.instrument_registry import (
+from ophydregistry.exceptions import (  # noqa: F401
     ComponentNotFound,
     InvalidComponentLabel,
     MultipleComponentsFound,
diff --git a/src/haven/instrument/aerotech.py b/src/haven/instrument/aerotech.py
index cff8803f..9fda4bb2 100644
--- a/src/haven/instrument/aerotech.py
+++ b/src/haven/instrument/aerotech.py
@@ -4,23 +4,21 @@
 import threading
 import time
 from collections import OrderedDict
-from datetime import datetime, timedelta
 from typing import Dict, Generator
 
 import numpy as np
 import pint
 from apstools.synApps.asyn import AsynRecord
 from ophyd import Component as Cpt
-from ophyd import Device, EpicsMotor, EpicsSignal
+from ophyd import EpicsMotor, EpicsSignal
 from ophyd import FormattedComponent as FCpt
-from ophyd import Kind, Signal, SignalRO, flyers
-from ophyd.status import AndStatus, StatusBase, SubscriptionStatus
+from ophyd import Kind, Signal, flyers
+from ophyd.status import SubscriptionStatus
 
 from .._iconfig import load_config
 from ..exceptions import InvalidScanParameters
 from .delay import DG645Delay
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
+from .device import aload_devices, make_device
 from .stage import XYStage
 
 log = logging.getLogger(__name__)
diff --git a/src/haven/instrument/aps.py b/src/haven/instrument/aps.py
index 574aff73..f71fd9a6 100644
--- a/src/haven/instrument/aps.py
+++ b/src/haven/instrument/aps.py
@@ -1,12 +1,9 @@
 import asyncio
 import logging
-import warnings
 
 from apsbss.apsbss_ophyd import EpicsBssDevice
 from apstools.devices.aps_machine import ApsMachineParametersDevice
 
-from haven import registry
-
 from .._iconfig import load_config
 from .device import aload_devices, make_device
 
diff --git a/src/haven/instrument/area_detector.py b/src/haven/instrument/area_detector.py
index f2ed8731..57ea4166 100644
--- a/src/haven/instrument/area_detector.py
+++ b/src/haven/instrument/area_detector.py
@@ -28,12 +28,11 @@
 )
 from ophyd.areadetector.plugins import StatsPlugin_V31 as OphydStatsPlugin_V31
 from ophyd.areadetector.plugins import StatsPlugin_V34 as OphydStatsPlugin_V34
-from ophyd.areadetector.plugins import TIFFPlugin_V31, TIFFPlugin_V34
+from ophyd.areadetector.plugins import TIFFPlugin_V31
 
 from .. import exceptions
 from .._iconfig import load_config
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
+from .device import aload_devices, make_device
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/camera.py b/src/haven/instrument/camera.py
index af41693f..88852750 100644
--- a/src/haven/instrument/camera.py
+++ b/src/haven/instrument/camera.py
@@ -1,17 +1,9 @@
 import asyncio
 import logging
-import warnings
-from typing import Optional, Sequence
+from typing import Sequence
 
 from ophyd import ADComponent as ADCpt
-from ophyd import (
-    CamBase,
-    EpicsSignal,
-    Kind,
-    SingleTrigger,
-    do_not_wait_for_lazy_connection,
-)
-from ophyd.areadetector.base import EpicsSignalWithRBV as SignalWithRBV
+from ophyd import CamBase, EpicsSignal, Kind, SingleTrigger
 from ophyd.areadetector.plugins import (
     ImagePlugin_V34,
     OverlayPlugin,
@@ -21,9 +13,8 @@
 
 from .. import exceptions
 from .._iconfig import load_config
-from .area_detector import AsyncCamMixin, DetectorBase, SimDetector, StatsPlugin_V34
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
+from .area_detector import AsyncCamMixin, DetectorBase, StatsPlugin_V34
+from .device import aload_devices, make_device
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/dxp.py b/src/haven/instrument/dxp.py
index 824c4ba2..599e8e88 100644
--- a/src/haven/instrument/dxp.py
+++ b/src/haven/instrument/dxp.py
@@ -1,31 +1,19 @@
 import asyncio
-import logging
 import time
 import warnings
 from collections import OrderedDict
 from enum import IntEnum
 from typing import Optional, Sequence
 
-from apstools.utils import cleanupText
-from ophyd import ADComponent as ADCpt
 from ophyd import Component as Cpt
-from ophyd import Device
 from ophyd import DynamicDeviceComponent as DDC
-from ophyd import EpicsSignal, EpicsSignalRO, Kind, OphydObject, Signal, flyers, mca
-from ophyd.areadetector.plugins import NetCDFPlugin_V34
-from ophyd.pseudopos import (
-    PseudoPositioner,
-    PseudoSingle,
-    pseudo_position_argument,
-    real_position_argument,
-)
+from ophyd import Kind, Signal, flyers, mca
 from ophyd.signal import DerivedSignal, InternalSignal
 from ophyd.status import StatusBase, SubscriptionStatus
 
 from .. import exceptions
 from .._iconfig import load_config
-from .device import RegexComponent as RECpt
-from .device import aload_devices, await_for_connection, make_device
+from .device import aload_devices, make_device
 from .fluorescence_detector import (
     MCASumMixin,
     ROIMixin,
@@ -34,8 +22,6 @@
     active_kind,
     add_roi_sums,
 )
-from .instrument_registry import registry
-from .scaler_triggered import ScalerTriggered
 
 __all__ = ["DxpDetector", "load_dxp"]
 
diff --git a/src/haven/instrument/fluorescence_detector.py b/src/haven/instrument/fluorescence_detector.py
index ab1d30c2..59f1c2ec 100644
--- a/src/haven/instrument/fluorescence_detector.py
+++ b/src/haven/instrument/fluorescence_detector.py
@@ -6,33 +6,15 @@
 
 """
 
-import asyncio
 import logging
-import time
-import warnings
 from collections import OrderedDict
-from contextlib import contextmanager
-from enum import IntEnum
 from typing import Optional, Sequence
 
 import numpy as np
 from apstools.utils import cleanupText
 from ophyd import Component as Cpt
-from ophyd import Device
-from ophyd import DynamicDeviceComponent as DDC
-from ophyd import EpicsSignal, EpicsSignalRO, Kind, Signal, SignalRO, flyers, mca
-from ophyd.areadetector.plugins import NetCDFPlugin_V34
+from ophyd import Device, Kind
 from ophyd.signal import DerivedSignal, InternalSignal
-from ophyd.status import StatusBase, SubscriptionStatus
-from pcdsdevices.signal import MultiDerivedSignal, MultiDerivedSignalRO
-from pcdsdevices.type_hints import OphydDataType, SignalToValue
-
-from .. import exceptions
-from .._iconfig import load_config
-from .device import RegexComponent as RECpt
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
-from .scaler_triggered import ScalerTriggered
 
 __all__ = ["XRFMixin"]
 
diff --git a/src/haven/instrument/heater.py b/src/haven/instrument/heater.py
index 429ddc2b..5a4586c6 100644
--- a/src/haven/instrument/heater.py
+++ b/src/haven/instrument/heater.py
@@ -7,8 +7,7 @@
 from ophyd import EpicsSignalRO, EpicsSignalWithRBV, PVPositioner
 
 from .._iconfig import load_config
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
+from .device import aload_devices, make_device
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/instrument_registry.py b/src/haven/instrument/instrument_registry.py
index f51ba215..a4f6f5c2 100644
--- a/src/haven/instrument/instrument_registry.py
+++ b/src/haven/instrument/instrument_registry.py
@@ -1,15 +1,6 @@
 import logging
-import warnings
-from itertools import chain
-from typing import Optional, Sequence
 
-from ophyd import ophydobj
 from ophydregistry import Registry as InstrumentRegistry
-from ophydregistry.exceptions import (
-    ComponentNotFound,
-    InvalidComponentLabel,
-    MultipleComponentsFound,
-)
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/ion_chamber.py b/src/haven/instrument/ion_chamber.py
index 4ce8be25..682d7a19 100644
--- a/src/haven/instrument/ion_chamber.py
+++ b/src/haven/instrument/ion_chamber.py
@@ -6,28 +6,17 @@
 import time
 import warnings
 from collections import OrderedDict
-from typing import Dict, Generator, Sequence
+from typing import Dict, Generator
 
-import epics
 import numpy as np
 import pint
 from apstools.devices import SRS570_PreAmplifier
 from ophyd import Component as Cpt
 from ophyd import Device, EpicsSignal, EpicsSignalRO
 from ophyd import FormattedComponent as FCpt
-from ophyd import (
-    Kind,
-    PseudoPositioner,
-    PseudoSingle,
-    PVPositioner,
-    PVPositionerPC,
-    Signal,
-    flyers,
-    status,
-)
+from ophyd import Kind, Signal, flyers, status
 from ophyd.mca import EpicsMCARecord
 from ophyd.ophydobj import OphydObject
-from ophyd.pseudopos import pseudo_position_argument, real_position_argument
 from ophyd.signal import DerivedSignal, InternalSignal
 from ophyd.status import SubscriptionStatus
 from ophyd.utils.errors import OpException
@@ -40,7 +29,7 @@
 from .epics import caget
 from .instrument_registry import registry
 from .labjack import AnalogInput
-from .scaler_triggered import ScalerSignal, ScalerSignalRO, ScalerTriggered
+from .scaler_triggered import ScalerSignalRO, ScalerTriggered
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/labjack.py b/src/haven/instrument/labjack.py
index a9f51610..2b3f8527 100644
--- a/src/haven/instrument/labjack.py
+++ b/src/haven/instrument/labjack.py
@@ -1,6 +1,5 @@
 from apstools.synApps import EpicsRecordDeviceCommonAll, EpicsRecordInputFields
-from ophyd import Component as Cpt
-from ophyd import Device, EpicsSignal, EpicsSignalRO
+from ophyd import Device, EpicsSignal
 from ophyd import FormattedComponent as FCpt
 from strenum import StrEnum
 
diff --git a/src/haven/instrument/lerix.py b/src/haven/instrument/lerix.py
index 819b4c74..b6858cd4 100644
--- a/src/haven/instrument/lerix.py
+++ b/src/haven/instrument/lerix.py
@@ -9,8 +9,7 @@
 from ophyd.pseudopos import pseudo_position_argument, real_position_argument
 
 from .._iconfig import load_config
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
+from .device import aload_devices, make_device
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/monochromator.py b/src/haven/instrument/monochromator.py
index c15e1763..d3dc38cf 100644
--- a/src/haven/instrument/monochromator.py
+++ b/src/haven/instrument/monochromator.py
@@ -4,11 +4,9 @@
 
 from ophyd import Component as Cpt
 from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
-from ophyd import FormattedComponent as FCpt
 
 from .._iconfig import load_config
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
+from .device import aload_devices, make_device
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/motor.py b/src/haven/instrument/motor.py
index ea507440..13d5d39b 100644
--- a/src/haven/instrument/motor.py
+++ b/src/haven/instrument/motor.py
@@ -1,19 +1,13 @@
 import asyncio
-import contextlib
-import io
 import logging
-import time
-from functools import partial
 from typing import Optional
 
-import epics
 from ophyd import Component as Cpt
-from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO, sim
+from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO
 
 from .._iconfig import load_config
-from .device import aload_devices, await_for_connection, make_device
+from .device import aload_devices, make_device
 from .epics import caget
-from .instrument_registry import registry
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/power_supply.py b/src/haven/instrument/power_supply.py
index 4d64ab01..78bb4621 100644
--- a/src/haven/instrument/power_supply.py
+++ b/src/haven/instrument/power_supply.py
@@ -1,8 +1,7 @@
 import asyncio
 import logging
 
-from ophyd import Component as Cpt
-from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
+from ophyd import Device, EpicsSignal, EpicsSignalRO
 from ophyd import FormattedComponent as FCpt
 
 from .._iconfig import load_config
diff --git a/src/haven/instrument/scaler_triggered.py b/src/haven/instrument/scaler_triggered.py
index 4c722848..a0988203 100644
--- a/src/haven/instrument/scaler_triggered.py
+++ b/src/haven/instrument/scaler_triggered.py
@@ -1,4 +1,4 @@
-from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, FormattedComponent
+from ophyd import EpicsSignal, EpicsSignalRO
 
 __all__ = ["ScalerTriggered"]
 
diff --git a/src/haven/instrument/shutter.py b/src/haven/instrument/shutter.py
index 8fafce0f..de258e61 100644
--- a/src/haven/instrument/shutter.py
+++ b/src/haven/instrument/shutter.py
@@ -2,13 +2,9 @@
 import logging
 
 from apstools.devices.shutters import ApsPssShutterWithStatus as Shutter
-from bluesky import suspenders
-from ophyd import EpicsSignal
-from ophyd import FormattedComponent as FCpt
 
 from .._iconfig import load_config
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
+from .device import aload_devices, make_device
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/stage.py b/src/haven/instrument/stage.py
index cc23aa17..aa63857c 100644
--- a/src/haven/instrument/stage.py
+++ b/src/haven/instrument/stage.py
@@ -1,25 +1,11 @@
 import asyncio
 import logging
-import math
-import threading
-import time
-from collections import OrderedDict
-from datetime import datetime, timedelta
-from typing import Dict, Generator
-
-import numpy as np
-import pint
-from apstools.synApps.asyn import AsynRecord
-from ophyd import Component as Cpt
-from ophyd import Device, EpicsMotor, EpicsSignal
+
+from ophyd import Device, EpicsMotor
 from ophyd import FormattedComponent as FCpt
-from ophyd import Kind, Signal, SignalRO, flyers
-from ophyd.status import AndStatus, StatusBase, SubscriptionStatus
 
 from .._iconfig import load_config
-from ..exceptions import InvalidScanParameters
-from .delay import DG645Delay
-from .device import aload_devices, await_for_connection, make_device
+from .device import aload_devices, make_device
 from .instrument_registry import registry
 
 __all__ = ["XYStage", "load_stages"]
diff --git a/src/haven/instrument/xray_source.py b/src/haven/instrument/xray_source.py
index 2177593d..9fa4847d 100644
--- a/src/haven/instrument/xray_source.py
+++ b/src/haven/instrument/xray_source.py
@@ -1,12 +1,10 @@
 import asyncio
 import logging
 
-import epics
 from apstools.devices.aps_undulator import ApsUndulator
 
 from .._iconfig import load_config
-from .device import aload_devices, await_for_connection, make_device
-from .instrument_registry import registry
+from .device import aload_devices, make_device
 
 log = logging.getLogger(__name__)
 
diff --git a/src/haven/instrument/xspress.py b/src/haven/instrument/xspress.py
index 136d3d84..ef155fa4 100644
--- a/src/haven/instrument/xspress.py
+++ b/src/haven/instrument/xspress.py
@@ -10,46 +10,19 @@
 import pandas as pd
 from apstools.devices import CamMixin_V34, SingleTrigger_V34
 from ophyd import ADComponent as ADCpt
-from ophyd import CamBase
 from ophyd import Component as Cpt
 from ophyd import DetectorBase, Device
 from ophyd import DynamicDeviceComponent as DDC
-from ophyd import (
-    EigerDetectorCam,
-    EpicsSignal,
-    EpicsSignalRO,
-    EpicsSignalWithRBV,
-    Kind,
-    Lambda750kCam,
-    OphydObject,
-    Signal,
-    SimDetectorCam,
-    SingleTrigger,
-)
+from ophyd import EpicsSignal, EpicsSignalRO, Kind
 from ophyd.areadetector.base import EpicsSignalWithRBV as SignalWithRBV
-from ophyd.areadetector.filestore_mixins import FileStoreHDF5IterativeWrite
-from ophyd.areadetector.plugins import (
-    HDF5Plugin_V31,
-    HDF5Plugin_V34,
-    ImagePlugin_V31,
-    ImagePlugin_V34,
-    OverlayPlugin,
-    PvaPlugin_V31,
-    PvaPlugin_V34,
-    ROIPlugin_V31,
-    ROIPlugin_V34,
-)
-from ophyd.areadetector.plugins import StatsPlugin_V31 as OphydStatsPlugin_V31
-from ophyd.areadetector.plugins import StatsPlugin_V34 as OphydStatsPlugin_V34
-from ophyd.areadetector.plugins import TIFFPlugin_V31, TIFFPlugin_V34
-from ophyd.signal import DerivedSignal, InternalSignal
-from ophyd.status import AndStatus, StatusBase, SubscriptionStatus
-from pcdsdevices.signal import MultiDerivedSignal, MultiDerivedSignalRO
+from ophyd.signal import InternalSignal
+from ophyd.status import StatusBase, SubscriptionStatus
+from pcdsdevices.signal import MultiDerivedSignal
 from pcdsdevices.type_hints import OphydDataType, SignalToValue
 
 from .._iconfig import load_config
 from .device import RegexComponent as RECpt
-from .device import aload_devices, await_for_connection, make_device
+from .device import aload_devices, make_device
 from .fluorescence_detector import (
     MCASumMixin,
     ROIMixin,
@@ -57,7 +30,6 @@
     XRFMixin,
     add_roi_sums,
 )
-from .instrument_registry import registry
 
 __all__ = ["load_xspress", "Xspress3Detector", "ROI"]
 
diff --git a/src/haven/plans/align_motor.py b/src/haven/plans/align_motor.py
index 32f52dd8..3f3f51f0 100644
--- a/src/haven/plans/align_motor.py
+++ b/src/haven/plans/align_motor.py
@@ -5,7 +5,7 @@
 from apstools.plans.alignment import lineup
 from bluesky import plan_stubs as bps
 from bluesky.callbacks import best_effort
-from bluesky.preprocessors import subs_decorator, subs_wrapper
+from bluesky.preprocessors import subs_wrapper
 
 from ..instrument.instrument_registry import registry
 from ..preprocessors import shutter_suspend_decorator
diff --git a/src/haven/plans/energy_scan.py b/src/haven/plans/energy_scan.py
index a10267a1..5db8e939 100644
--- a/src/haven/plans/energy_scan.py
+++ b/src/haven/plans/energy_scan.py
@@ -3,18 +3,16 @@
 """
 
 import logging
-import warnings
 from collections import ChainMap
 from typing import Mapping, Sequence, Union
 
 import numpy as np
 from bluesky import plans as bp
 
-from .. import merge_ranges
 from .._iconfig import load_config
 from ..constants import edge_energy
 from ..instrument import registry
-from ..preprocessors import baseline_decorator, shutter_suspend_decorator
+from ..preprocessors import baseline_decorator
 from ..typing import DetectorList
 
 __all__ = ["energy_scan"]
diff --git a/src/haven/plans/fly.py b/src/haven/plans/fly.py
index 42127e47..e2dc4cad 100644
--- a/src/haven/plans/fly.py
+++ b/src/haven/plans/fly.py
@@ -1,6 +1,5 @@
 from collections import OrderedDict, abc
-from functools import partial
-from typing import Mapping, Optional, Sequence, Union
+from typing import Mapping, Sequence, Union
 
 import numpy as np
 from bluesky import plan_patterns
@@ -11,8 +10,6 @@
 from ophyd.flyers import FlyerInterface
 from ophyd.status import StatusBase
 
-from ..preprocessors import baseline_decorator, shutter_suspend_decorator
-
 __all__ = ["fly_scan", "grid_fly_scan"]
 
 
diff --git a/src/haven/plans/mono_ID_calibration.py b/src/haven/plans/mono_ID_calibration.py
index 798f9f99..54c7fa39 100644
--- a/src/haven/plans/mono_ID_calibration.py
+++ b/src/haven/plans/mono_ID_calibration.py
@@ -3,7 +3,6 @@
 from typing import Optional, Sequence
 
 import pandas as pd
-from bluesky import plan_stubs as bps
 from bluesky.callbacks.best_effort import BestEffortCallback
 from lmfit.model import Model
 from lmfit.models import QuadraticModel
diff --git a/src/haven/plans/mono_gap_calibration.py b/src/haven/plans/mono_gap_calibration.py
index 8008e04b..149293aa 100644
--- a/src/haven/plans/mono_gap_calibration.py
+++ b/src/haven/plans/mono_gap_calibration.py
@@ -2,8 +2,6 @@
 import logging
 from pathlib import Path
 
-from apstools.plans.alignment import lineup
-from bluesky import plan_stubs as bps
 from bluesky.callbacks import LiveFit, best_effort, fitting, mpl_plotting
 from bluesky.preprocessors import subs_decorator
 from lmfit.models import StepModel
diff --git a/src/haven/plans/record_dark_current.py b/src/haven/plans/record_dark_current.py
index bf09f36d..82c413b8 100644
--- a/src/haven/plans/record_dark_current.py
+++ b/src/haven/plans/record_dark_current.py
@@ -1,5 +1,4 @@
 from bluesky import plan_stubs as bps
-from bluesky import plans as bp
 
 from ..instrument.instrument_registry import registry
 from .shutters import close_shutters, open_shutters
diff --git a/src/haven/plans/set_energy.py b/src/haven/plans/set_energy.py
index ae051e5c..a40aff4a 100644
--- a/src/haven/plans/set_energy.py
+++ b/src/haven/plans/set_energy.py
@@ -1,7 +1,6 @@
-from typing import Sequence, Union
+from typing import Union
 
 from bluesky import plan_stubs as bps
-from ophyd import Device
 
 from .. import exceptions
 from ..instrument.instrument_registry import registry
diff --git a/src/haven/plans/xafs_scan.py b/src/haven/plans/xafs_scan.py
index 86efd505..094b12e2 100644
--- a/src/haven/plans/xafs_scan.py
+++ b/src/haven/plans/xafs_scan.py
@@ -8,11 +8,9 @@
 from collections import ChainMap
 from typing import Mapping, Optional, Sequence, Union
 
-import numpy as np
-
 from .. import exceptions
 from ..energy_ranges import ERange, KRange, merge_ranges
-from ..preprocessors import baseline_decorator, shutter_suspend_decorator
+from ..preprocessors import baseline_decorator
 from ..typing import DetectorList
 from .energy_scan import energy_scan
 
diff --git a/src/haven/preprocessors.py b/src/haven/preprocessors.py
index 2acaebfb..d9b79f05 100644
--- a/src/haven/preprocessors.py
+++ b/src/haven/preprocessors.py
@@ -12,7 +12,7 @@
 import epics
 import pkg_resources
 from bluesky.preprocessors import baseline_wrapper as bluesky_baseline_wrapper
-from bluesky.preprocessors import finalize_wrapper, msg_mutator, suspend_wrapper
+from bluesky.preprocessors import finalize_wrapper, msg_mutator
 from bluesky.suspenders import SuspendBoolLow
 from bluesky.utils import Msg, make_decorator
 
diff --git a/src/haven/run_engine.py b/src/haven/run_engine.py
index bd8d266b..3de001bb 100644
--- a/src/haven/run_engine.py
+++ b/src/haven/run_engine.py
@@ -1,12 +1,10 @@
 import logging
-import warnings
 
 import databroker
 from bluesky import RunEngine as BlueskyRunEngine
 from bluesky import suspenders
 from bluesky.callbacks.best_effort import BestEffortCallback
 
-from ._iconfig import load_config
 from .exceptions import ComponentNotFound
 from .instrument.instrument_registry import registry
 from .preprocessors import inject_haven_md_wrapper
@@ -14,26 +12,6 @@
 log = logging.getLogger(__name__)
 
 
-# class RunEngine(BlueskyRunEngine):
-#     def __init__(self, *args, connect_databroker=True, **kwargs):
-#         super().__init__(*args, **kwargs)
-#         if connect_databroker:
-#             # Load the databroker catalog and set up data saving
-#             catalog_name = load_config()["database"]["databroker"]["catalog"]
-#             try:
-#                 catalog = databroker.catalog[catalog_name]
-#                 self.subscribe(catalog.v1.insert)
-#             except Exception as e:
-#                 msg = (
-#                     f"Data are not being saved! Could not load databroker catalog: {e}"
-#                 )
-#                 log.error(msg)
-#                 warnings.warn(msg)
-#                 raise RuntimeError(msg)
-#         # Add metadata pre-processor
-#         self.preprocessors.append(inject_haven_md_wrapper)
-
-
 catalog = None
 
 
diff --git a/src/haven/tests/ioc_apsbss.py b/src/haven/tests/ioc_apsbss.py
index cbf84b77..5c581f00 100644
--- a/src/haven/tests/ioc_apsbss.py
+++ b/src/haven/tests/ioc_apsbss.py
@@ -1,17 +1,12 @@
 #!/usr/bin/env python3
-from caproto import ChannelType
 from caproto.server import (
     PVGroup,
     PvpropertyInteger,
     PvpropertyString,
-    SubGroup,
     ioc_arg_parser,
     pvproperty,
     run,
 )
-from ophyd.tests.fake_motor_ioc import FakeMotorIOC
-
-from haven.simulated_ioc import ResponsiveMotorFields  # , IOC as IOC_
 
 
 class SimpleGroup(PVGroup):
diff --git a/src/haven/tests/ioc_area_detector.py b/src/haven/tests/ioc_area_detector.py
index 01eafc6e..4f15f933 100644
--- a/src/haven/tests/ioc_area_detector.py
+++ b/src/haven/tests/ioc_area_detector.py
@@ -7,7 +7,6 @@
     pvproperty,
     run,
 )
-from ophyd.tests.scaler_ioc import EpicsScalerGroup
 
 
 class AreaDetectorGroup(PVGroup):
diff --git a/src/haven/tests/ioc_dxp.py b/src/haven/tests/ioc_dxp.py
index bb8a99ec..69aa0d5c 100755
--- a/src/haven/tests/ioc_dxp.py
+++ b/src/haven/tests/ioc_dxp.py
@@ -7,7 +7,6 @@
 from caproto import ChannelType
 from caproto.server import (
     PVGroup,
-    PvpropertyDouble,
     SubGroup,
     get_pv_pair_wrapper,
     ioc_arg_parser,
diff --git a/src/haven/tests/ioc_mono.py b/src/haven/tests/ioc_mono.py
index 16fec2d1..55d72c35 100644
--- a/src/haven/tests/ioc_mono.py
+++ b/src/haven/tests/ioc_mono.py
@@ -1,7 +1,5 @@
 #!/usr/bin/env python3
-from caproto import ChannelType
-from caproto.server import PVGroup, SubGroup, ioc_arg_parser, pvproperty, run
-from ophyd.tests.fake_motor_ioc import FakeMotorIOC
+from caproto.server import PVGroup, ioc_arg_parser, pvproperty, run
 
 from haven.simulated_ioc import ResponsiveMotorFields  # , IOC as IOC_
 
diff --git a/src/haven/tests/ioc_motor.py b/src/haven/tests/ioc_motor.py
index 53b791a5..9dce4692 100644
--- a/src/haven/tests/ioc_motor.py
+++ b/src/haven/tests/ioc_motor.py
@@ -1,6 +1,5 @@
 #!/usr/bin/env python3
-from caproto import ChannelType
-from caproto.server import PVGroup, SubGroup, ioc_arg_parser, pvproperty, run
+from caproto.server import PVGroup, ioc_arg_parser, pvproperty, run
 
 from haven.simulated_ioc import ResponsiveMotorFields
 
diff --git a/src/haven/tests/ioc_preamp.py b/src/haven/tests/ioc_preamp.py
index 0eff2300..02d5b83c 100644
--- a/src/haven/tests/ioc_preamp.py
+++ b/src/haven/tests/ioc_preamp.py
@@ -1,17 +1,6 @@
 #!/usr/bin/env python3
 from caproto import ChannelType
-from caproto.server import (
-    PVGroup,
-    PvpropertyInteger,
-    PvpropertyString,
-    SubGroup,
-    ioc_arg_parser,
-    pvproperty,
-    run,
-)
-from ophyd.tests.fake_motor_ioc import FakeMotorIOC
-
-from haven.simulated_ioc import ResponsiveMotorFields  # , IOC as IOC_
+from caproto.server import PVGroup, SubGroup, ioc_arg_parser, pvproperty, run
 
 
 class PreampsGroup(PVGroup):
diff --git a/src/haven/tests/ioc_ptc10.py b/src/haven/tests/ioc_ptc10.py
index 60f2255a..1f06ea64 100644
--- a/src/haven/tests/ioc_ptc10.py
+++ b/src/haven/tests/ioc_ptc10.py
@@ -1,7 +1,5 @@
 #!/usr/bin/env python3
-from caproto import ChannelType
 from caproto.server import PVGroup, SubGroup, ioc_arg_parser, pvproperty, run
-from ophyd.tests.scaler_ioc import EpicsScalerGroup
 
 
 class PTC10Group(PVGroup):
diff --git a/src/haven/tests/ioc_scaler.py b/src/haven/tests/ioc_scaler.py
index b2d87c6d..2fca9c03 100644
--- a/src/haven/tests/ioc_scaler.py
+++ b/src/haven/tests/ioc_scaler.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python3
-from caproto import ChannelType
 from caproto.server import PVGroup, SubGroup, ioc_arg_parser, pvproperty, run
 from ophyd.tests.scaler_ioc import EpicsScalerGroup
 
diff --git a/src/haven/tests/ioc_simple.py b/src/haven/tests/ioc_simple.py
index d20b547d..c60d3029 100644
--- a/src/haven/tests/ioc_simple.py
+++ b/src/haven/tests/ioc_simple.py
@@ -1,16 +1,5 @@
 #!/usr/bin/env python3
-from caproto import ChannelType
-from caproto.server import (
-    PVGroup,
-    PvpropertyDouble,
-    SubGroup,
-    ioc_arg_parser,
-    pvproperty,
-    run,
-)
-from ophyd.tests.fake_motor_ioc import FakeMotorIOC
-
-from haven.simulated_ioc import ResponsiveMotorFields  # , IOC as IOC_
+from caproto.server import PVGroup, ioc_arg_parser, pvproperty, run
 
 
 class SimpleGroup(PVGroup):
diff --git a/src/haven/tests/ioc_undulator.py b/src/haven/tests/ioc_undulator.py
index 5967d83b..6b805aa4 100644
--- a/src/haven/tests/ioc_undulator.py
+++ b/src/haven/tests/ioc_undulator.py
@@ -1,14 +1,5 @@
 #!/usr/bin/env python3
-from caproto import ChannelType
-from caproto.server import (
-    PVGroup,
-    PvpropertyDouble,
-    SubGroup,
-    ioc_arg_parser,
-    pvproperty,
-    run,
-)
-from ophyd.tests.fake_motor_ioc import FakeMotorIOC
+from caproto.server import PVGroup, PvpropertyDouble, ioc_arg_parser, pvproperty, run
 
 from haven.simulated_ioc import ResponsiveMotorFields  # , IOC as IOC_
 
diff --git a/src/haven/tests/test_align_motor.py b/src/haven/tests/test_align_motor.py
index 1a1ee550..2719fbc5 100644
--- a/src/haven/tests/test_align_motor.py
+++ b/src/haven/tests/test_align_motor.py
@@ -1,13 +1,9 @@
-import time
-
-import matplotlib.pyplot as plt
 import pytest
 from bluesky import RunEngine
 from bluesky.callbacks.best_effort import BestEffortCallback
-from bluesky.callbacks.fitting import PeakStats
 from ophyd import sim
 
-from haven import align_motor, align_pitch2, registry
+from haven import align_motor
 
 # from run_engine import RunEngineStub
 
diff --git a/src/haven/tests/test_aps.py b/src/haven/tests/test_aps.py
index 8d7bd5eb..0a012a78 100644
--- a/src/haven/tests/test_aps.py
+++ b/src/haven/tests/test_aps.py
@@ -1,8 +1,4 @@
-from unittest import mock
-
-import pytest
-
-from haven.instrument import aps, device
+from haven.instrument import aps
 
 
 def test_load_aps(sim_registry):
diff --git a/src/haven/tests/test_area_detector.py b/src/haven/tests/test_area_detector.py
index 6a4488a4..13c84540 100644
--- a/src/haven/tests/test_area_detector.py
+++ b/src/haven/tests/test_area_detector.py
@@ -1,5 +1,3 @@
-from ophyd.device import do_not_wait_for_lazy_connection
-
 from haven.instrument.area_detector import load_area_detectors
 
 
diff --git a/src/haven/tests/test_beam_properties.py b/src/haven/tests/test_beam_properties.py
index 92f08fe0..8d05ec51 100644
--- a/src/haven/tests/test_beam_properties.py
+++ b/src/haven/tests/test_beam_properties.py
@@ -1,4 +1,3 @@
-import matplotlib.pyplot as plt
 import numpy as np
 import pytest
 from ophyd.sim import det1, det2, motor1
diff --git a/src/haven/tests/test_energy_positioner.py b/src/haven/tests/test_energy_positioner.py
index 29291b60..c33a7165 100644
--- a/src/haven/tests/test_energy_positioner.py
+++ b/src/haven/tests/test_energy_positioner.py
@@ -1,6 +1,5 @@
 import time
 
-import epics
 import pytest
 from ophyd.sim import instantiate_fake_device
 
diff --git a/src/haven/tests/test_energy_xafs_scan.py b/src/haven/tests/test_energy_xafs_scan.py
index bcbac538..c6eac4b8 100644
--- a/src/haven/tests/test_energy_xafs_scan.py
+++ b/src/haven/tests/test_energy_xafs_scan.py
@@ -1,10 +1,8 @@
-import time
-
 import numpy as np
 import pytest
 from ophyd import sim
 
-from haven import KRange, align_slits, energy_scan, registry, xafs_scan
+from haven import KRange, energy_scan, xafs_scan
 
 
 @pytest.fixture()
diff --git a/src/haven/tests/test_fluorescence_detectors.py b/src/haven/tests/test_fluorescence_detectors.py
index 71133017..5d6f8ee9 100644
--- a/src/haven/tests/test_fluorescence_detectors.py
+++ b/src/haven/tests/test_fluorescence_detectors.py
@@ -6,18 +6,13 @@
 
 """
 
-import asyncio
 import logging
 import time
 from pathlib import Path
-from unittest.mock import MagicMock
 
 import numpy as np
 import pytest
-from bluesky import plans as bp
-from epics import caget
-from ophyd import DynamicDeviceComponent as DDC
-from ophyd import Kind, OphydObject, Signal
+from ophyd import OphydObject, Signal
 
 from haven.instrument.dxp import load_dxp, parse_xmap_buffer
 from haven.instrument.xspress import load_xspress
diff --git a/src/haven/tests/test_fly_plans.py b/src/haven/tests/test_fly_plans.py
index b808d9b6..50848701 100644
--- a/src/haven/tests/test_fly_plans.py
+++ b/src/haven/tests/test_fly_plans.py
@@ -2,7 +2,6 @@
 from unittest.mock import MagicMock
 
 import numpy as np
-from bluesky import plan_patterns
 from ophyd import sim
 
 from haven.plans.fly import FlyerCollector, fly_scan, grid_fly_scan
diff --git a/src/haven/tests/test_heater.py b/src/haven/tests/test_heater.py
index b5061a0c..0eeed293 100644
--- a/src/haven/tests/test_heater.py
+++ b/src/haven/tests/test_heater.py
@@ -1,8 +1,4 @@
-import time
-
-from epics import caget, caput
-
-from haven.instrument.heater import CapillaryHeater, load_heaters
+from haven.instrument.heater import load_heaters
 
 PREFIX = "255idptc10:"
 
diff --git a/src/haven/tests/test_instrument_registry.py b/src/haven/tests/test_instrument_registry.py
index 0560b937..0f85176c 100644
--- a/src/haven/tests/test_instrument_registry.py
+++ b/src/haven/tests/test_instrument_registry.py
@@ -2,7 +2,7 @@
 from ophyd import Device, EpicsMotor, sim
 
 from haven.instrument import InstrumentRegistry
-from haven.instrument.instrument_registry import (
+from haven.exceptions import (
     ComponentNotFound,
     MultipleComponentsFound,
 )
diff --git a/src/haven/tests/test_ion_chamber.py b/src/haven/tests/test_ion_chamber.py
index 8fcc113e..d0501139 100644
--- a/src/haven/tests/test_ion_chamber.py
+++ b/src/haven/tests/test_ion_chamber.py
@@ -1,11 +1,8 @@
 import time
-import warnings
 
-import epics
 import numpy as np
 import pytest
 
-from haven import exceptions
 from haven.instrument import ion_chamber
 
 
diff --git a/src/haven/tests/test_lerix.py b/src/haven/tests/test_lerix.py
index 4908c7d8..74224086 100644
--- a/src/haven/tests/test_lerix.py
+++ b/src/haven/tests/test_lerix.py
@@ -1,11 +1,8 @@
 import time
-from unittest import mock
 
 import pytest
-from epics import caget
 from ophyd.sim import instantiate_fake_device
 
-import haven
 from haven.instrument import lerix
 
 um_per_mm = 1000
diff --git a/src/haven/tests/test_mono_ID_calibration_plan.py b/src/haven/tests/test_mono_ID_calibration_plan.py
index 338b0ab7..9558ede4 100644
--- a/src/haven/tests/test_mono_ID_calibration_plan.py
+++ b/src/haven/tests/test_mono_ID_calibration_plan.py
@@ -1,8 +1,6 @@
 from unittest.mock import MagicMock
 
 import pytest
-from bluesky.callbacks.best_effort import BestEffortCallback
-from lmfit.models import QuadraticModel
 from ophyd import sim
 
 from haven import mono_ID_calibration
diff --git a/src/haven/tests/test_monochromator.py b/src/haven/tests/test_monochromator.py
index c7b86783..701286ab 100644
--- a/src/haven/tests/test_monochromator.py
+++ b/src/haven/tests/test_monochromator.py
@@ -1,7 +1,3 @@
-import time
-
-import epics
-
 from haven import Monochromator
 
 
diff --git a/src/haven/tests/test_motor.py b/src/haven/tests/test_motor.py
index f3f5b124..d2eafb38 100644
--- a/src/haven/tests/test_motor.py
+++ b/src/haven/tests/test_motor.py
@@ -1,5 +1,3 @@
-import epics
-
 from haven.instrument import motor
 
 
diff --git a/src/haven/tests/test_plans.py b/src/haven/tests/test_plans.py
index 294eaefa..05bef29b 100644
--- a/src/haven/tests/test_plans.py
+++ b/src/haven/tests/test_plans.py
@@ -4,14 +4,12 @@
 
 """
 
-import unittest
 import warnings
 
-import numpy as np
 import pytest
 from ophyd import sim
 
-from haven import KRange, align_slits, energy_scan, registry, xafs_scan
+from haven import align_slits
 
 
 def test_align_slits(RE):
diff --git a/src/haven/tests/test_preprocessors.py b/src/haven/tests/test_preprocessors.py
index dbce42e3..60ff8fc0 100644
--- a/src/haven/tests/test_preprocessors.py
+++ b/src/haven/tests/test_preprocessors.py
@@ -1,14 +1,11 @@
-import os
 from unittest.mock import MagicMock
 
-import pytest
 from bluesky import plans as bp
 from bluesky.callbacks import CallbackBase
-from bluesky.simulators import summarize_plan
-from ophyd.sim import SynAxis, det, instantiate_fake_device, make_fake_device, motor
+from ophyd.sim import SynAxis, det, instantiate_fake_device
 
-from haven import baseline_decorator, baseline_wrapper, plans, run_engine
-from haven.instrument.aps import ApsMachine, EpicsBssDevice, load_aps
+from haven import baseline_decorator, baseline_wrapper, run_engine
+from haven.instrument.aps import EpicsBssDevice
 from haven.preprocessors import shutter_suspend_decorator, shutter_suspend_wrapper
 
 
diff --git a/src/haven/tests/test_save_motor_positions.py b/src/haven/tests/test_save_motor_positions.py
index 5aa9663f..dcadb19f 100644
--- a/src/haven/tests/test_save_motor_positions.py
+++ b/src/haven/tests/test_save_motor_positions.py
@@ -1,24 +1,20 @@
 import datetime as dt
 import logging
-import os
 import time
 from datetime import datetime
 from zoneinfo import ZoneInfo
 
 import pytest
-import pytz
 import time_machine
 from ophyd import Component as Cpt
-from ophyd import EpicsMotor, Signal
-from ophyd.sim import SynAxis, make_fake_device, motor1
+from ophyd import Signal
+from ophyd.sim import SynAxis, motor1
 
 from haven import (
-    HavenMotor,
     get_motor_position,
     list_current_motor_positions,
     list_motor_positions,
     recall_motor_position,
-    registry,
     save_motor_position,
 )
 
diff --git a/src/haven/tests/test_shutter.py b/src/haven/tests/test_shutter.py
index 2bf9d07d..fca9cd8d 100644
--- a/src/haven/tests/test_shutter.py
+++ b/src/haven/tests/test_shutter.py
@@ -1,5 +1,3 @@
-from unittest import mock
-
 from haven import registry
 from haven.instrument import shutter
 
diff --git a/src/haven/tests/test_stages.py b/src/haven/tests/test_stages.py
index 663ae64e..e875e7bb 100644
--- a/src/haven/tests/test_stages.py
+++ b/src/haven/tests/test_stages.py
@@ -1,14 +1,7 @@
 """Tests for a generic X-Y stage."""
 
 
-import time
-from collections import OrderedDict
-from datetime import datetime
-from unittest import mock
-
-import numpy as np
 import pytest
-from ophyd.sim import instantiate_fake_device, make_fake_device
 
 from haven import exceptions, registry
 from haven.instrument import stage
diff --git a/src/haven/tests/test_xray_source.py b/src/haven/tests/test_xray_source.py
index 17404e23..03a65dcd 100644
--- a/src/haven/tests/test_xray_source.py
+++ b/src/haven/tests/test_xray_source.py
@@ -1,6 +1,3 @@
-from unittest import mock
-
-import haven
 from haven.instrument.xray_source import load_xray_sources
 
 
diff --git a/src/haven/tests/test_xspress.py b/src/haven/tests/test_xspress.py
index 72e22a30..dbb21bf9 100644
--- a/src/haven/tests/test_xspress.py
+++ b/src/haven/tests/test_xspress.py
@@ -1,6 +1,4 @@
 import pytest
-from ophyd.device import do_not_wait_for_lazy_connection
-from ophyd.sim import make_fake_device
 
 from haven.instrument.xspress import Xspress3Detector
 
diff --git a/tests/test_simulated_ioc.py b/tests/test_simulated_ioc.py
index a0551ece..f10deb88 100644
--- a/tests/test_simulated_ioc.py
+++ b/tests/test_simulated_ioc.py
@@ -1,13 +1,10 @@
 import logging
 import time
 from pathlib import Path
-import os
 
 import pytest
 from epics import caget, caput
 
-from haven.simulated_ioc import simulated_ioc
-
 
 log = logging.getLogger(__name__)
 

From 3731b7144d20bf20a02b3520828dc7a31f636d64 Mon Sep 17 00:00:00 2001
From: Sector 25 ID-C User <s25idcuser@microprobe.xray.aps.anl.gov>
Date: Thu, 16 Nov 2023 10:36:13 -0600
Subject: [PATCH 8/9] Fixed bugs in firefly, and re-organized the main window
 menus.

---
 src/firefly/application.py          | 18 ++++++--
 src/firefly/main_window.py          | 69 +++++++++--------------------
 src/haven/instrument/camera.py      |  2 +-
 src/haven/instrument/ion_chamber.py | 19 ++++----
 src/haven/instrument/motor.py       |  1 -
 5 files changed, 48 insertions(+), 61 deletions(-)

diff --git a/src/firefly/application.py b/src/firefly/application.py
index 0d95eded..54947097 100644
--- a/src/firefly/application.py
+++ b/src/firefly/application.py
@@ -46,6 +46,8 @@ class FireflyApplication(PyDMApplication):
     show_runs_window_action: QtWidgets.QAction
     show_energy_window_action: QtWidgets.QAction
     show_bss_window_action: QtWidgets.QAction
+    show_voltmeters_window_action: QtWidgets.QAction
+    show_logs_window_action: QtWidgets.QAction
     launch_queuemonitor_action: QtWidgets.QAction
 
     # Keep track of motors
@@ -169,6 +171,18 @@ def setup_window_actions(self):
             text="Scheduling (&BSS)",
             slot=self.show_bss_window,
         )
+        # Launch ion chamber voltmeters window
+        self._setup_window_action(
+            action_name="show_voltmeters_window_action",
+            text="&Voltmeters",
+            slot=self.show_voltmeters_window,
+        )
+        # Launch log window
+        self._setup_window_action(
+            action_name="show_logs_window_action",
+            text="Logs",
+            slot=self.show_logs_window,
+        )
         # Launch energy window
         self._setup_window_action(
             action_name="show_energy_window_action",
@@ -427,9 +441,7 @@ def connect_menu_signals(self, window):
         and setup code.
 
         """
-        window.actionShow_Log_Viewer.triggered.connect(self.show_log_viewer_window)
         window.actionShow_Xafs_Scan.triggered.connect(self.show_xafs_scan_window)
-        window.actionShow_Voltmeters.triggered.connect(self.show_voltmeters_window)
         window.actionShow_Sample_Viewer.triggered.connect(
             self.show_sample_viewer_window
         )
@@ -538,7 +550,7 @@ def show_run_browser(self):
         )
 
     @QtCore.Slot()
-    def show_log_viewer_window(self):
+    def show_logs_window(self):
         self.show_window(FireflyMainWindow, ui_dir / "log_viewer.ui", name="log_viewer")
 
     @QtCore.Slot()
diff --git a/src/firefly/main_window.py b/src/firefly/main_window.py
index f7bf69cc..340bea90 100644
--- a/src/firefly/main_window.py
+++ b/src/firefly/main_window.py
@@ -106,6 +106,8 @@ def customize_ui(self):
         # Connect signals to the status bar
         app.queue_environment_state_changed.connect(self.ui.environment_label.setText)
         app.queue_re_state_changed.connect(self.ui.re_label.setText)
+        # Log viewer window
+        self.ui.menuView.addAction(app.show_logs_window_action)
         # Setup menu
         self.ui.menuSetup = QtWidgets.QMenu(self.ui.menubar)
         self.ui.menuSetup.setObjectName("menuSetup")
@@ -119,36 +121,15 @@ def customize_ui(self):
         for action in app.queue_action_group.actions():
             self.ui.queue_menu.addAction(action)
         self.ui.queue_menu.addSeparator()
+        # Queue settings for the queue client
+        self.ui.queue_menu.addAction(app.launch_queuemonitor_action)
         self.ui.queue_menu.addAction(app.queue_autoplay_action)
         self.ui.queue_menu.addAction(app.queue_open_environment_action)
-        self.ui.menuView.addAction(app.launch_queuemonitor_action)
-        # Log viewer window
-        self.add_menu_action(
-            action_name="actionShow_Log_Viewer", text="Logs", menu=self.ui.menuView
-        )
         # Positioners menu
         self.ui.menuPositioners = QtWidgets.QMenu(self.ui.menubar)
         self.ui.menuPositioners.setObjectName("menuPositioners")
         self.ui.menuPositioners.setTitle("&Positioners")
         self.ui.menubar.addAction(self.ui.menuPositioners.menuAction())
-        # Detectors menu
-        self.ui.menuDetectors = QtWidgets.QMenu(self.ui.menubar)
-        self.ui.menuDetectors.setObjectName("menuDetectors")
-        self.ui.menuDetectors.setTitle("&Detectors")
-        self.ui.menubar.addAction(self.ui.menuDetectors.menuAction())
-        # Scans menu
-        self.ui.menuScans = QtWidgets.QMenu(self.ui.menubar)
-        self.ui.menuScans.setObjectName("menuScans")
-        self.ui.menuScans.setTitle("Scans")
-        self.ui.menubar.addAction(self.ui.menuScans.menuAction())
-        # Add entries for general scan management
-        self.ui.menuScans.addAction(app.launch_queuemonitor_action)
-        self.ui.menuScans.addAction(app.show_run_browser_action)
-        self.ui.menuScans.addSeparator()
-        # XAFS scan window
-        self.add_menu_action(
-            action_name="actionShow_Log_Viewer", text="Logs", menu=self.ui.menuView
-        )
         # Sample viewer
         self.add_menu_action(
             action_name="actionShow_Sample_Viewer",
@@ -172,36 +153,32 @@ def customize_ui(self):
             text="&XAFS Scan",
             menu=self.ui.menuScans,
         )
-        # Auto-play setting for the queue client
-        if hasattr(app, "queue_autoplay_action"):
-            self.ui.menuScans.addAction(app.queue_autoplay_action)
+        # Add entries for general scan management
+        self.ui.menuScans.addSeparator()
+        self.ui.menuScans.addAction(app.show_run_browser_action)
         # Detectors menu
-        self.ui.menuDetectors = QtWidgets.QMenu(self.ui.menubar)
-        self.ui.menuDetectors.setObjectName("menuDetectors")
-        self.ui.menuDetectors.setTitle("&Detectors")
-        self.ui.menubar.addAction(self.ui.menuDetectors.menuAction())
+        self.ui.detectors_menu = QtWidgets.QMenu(self.ui.menubar)
+        self.ui.detectors_menu.setObjectName("detectors_menu")
+        self.ui.detectors_menu.setTitle("&Detectors")
+        self.ui.menubar.addAction(self.ui.detectors_menu.menuAction())
         # Voltmeters window
-        self.add_menu_action(
-            action_name="actionShow_Voltmeters",
-            text="&Voltmeters",
-            menu=self.ui.menuDetectors,
-        )
+        self.ui.detectors_menu.addAction(app.show_voltmeters_window_action)
         # Add actions to the motors sub-menus
         for action in app.motor_actions:
             self.ui.menuMotors.addAction(action)
         # Add an ion chamber sub-menu
-        self.ui.menuIonChambers = QtWidgets.QMenu(self.ui.menubar)
-        self.ui.menuIonChambers.setObjectName("menuIonChambers")
-        self.ui.menuIonChambers.setTitle("&Ion Chambers")
-        self.ui.menuDetectors.addAction(self.ui.menuIonChambers.menuAction())
+        self.ui.ion_chambers_menu = QtWidgets.QMenu(self.ui.menubar)
+        self.ui.ion_chambers_menu.setObjectName("ion_chambers_menu")
+        self.ui.ion_chambers_menu.setTitle("&Ion Chambers")
+        self.ui.detectors_menu.addAction(self.ui.ion_chambers_menu.menuAction())
         # Add actions for the individual ion chambers
         for action in app.ion_chamber_actions.values():
-            self.ui.menuIonChambers.addAction(action)
+            self.ui.ion_chambers_menu.addAction(action)
         # Cameras sub-menu
         self.ui.menuCameras = QtWidgets.QMenu(self.ui.menubar)
         self.ui.menuCameras.setObjectName("menuCameras")
         self.ui.menuCameras.setTitle("Cameras")
-        self.ui.menuDetectors.addAction(self.ui.menuCameras.menuAction())
+        self.ui.detectors_menu.addAction(self.ui.menuCameras.menuAction())
         # Add actions to the cameras sub-menus
         self.ui.menuCameras.addAction(app.show_cameras_window_action)
         self.ui.menuCameras.addSeparator()
@@ -210,15 +187,15 @@ def customize_ui(self):
         # Add area detectors to detectors menu
         ad_actions = app.area_detector_actions.values()
         if len(ad_actions) > 0:
-            self.ui.menuDetectors.addSeparator()
+            self.ui.detectors_menu.addSeparator()
         for action in ad_actions:
-            self.ui.menuDetectors.addAction(action)
+            self.ui.detectors_menu.addAction(action)
         # Add XRF detectors to detectors menu
         xrf_actions = app.xrf_detector_actions.values()
         if len(xrf_actions) > 0:
-            self.ui.menuDetectors.addSeparator()
+            self.ui.detectors_menu.addSeparator()
         for action in xrf_actions:
-            self.ui.menuDetectors.addAction(action)
+            self.ui.detectors_menu.addAction(action)
         # Add other menu actions
         self.ui.menuView.addAction(app.show_status_window_action)
         self.ui.menuSetup.addAction(app.show_bss_window_action)
@@ -244,9 +221,7 @@ def update_window_title(self):
 
     def export_actions(self):
         """Expose specific signals that might be useful for responding to window changes."""
-        self.actionShow_Log_Viewer = self.ui.actionShow_Log_Viewer
         self.actionShow_Xafs_Scan = self.ui.actionShow_Xafs_Scan
-        self.actionShow_Voltmeters = self.ui.actionShow_Voltmeters
 
 
 class PlanMainWindow(FireflyMainWindow):
diff --git a/src/haven/instrument/camera.py b/src/haven/instrument/camera.py
index 88852750..219dac63 100644
--- a/src/haven/instrument/camera.py
+++ b/src/haven/instrument/camera.py
@@ -13,7 +13,7 @@
 
 from .. import exceptions
 from .._iconfig import load_config
-from .area_detector import AsyncCamMixin, DetectorBase, StatsPlugin_V34
+from .area_detector import AsyncCamMixin, DetectorBase, SimDetector, StatsPlugin_V34  # noqa: F401
 from .device import aload_devices, make_device
 
 log = logging.getLogger(__name__)
diff --git a/src/haven/instrument/ion_chamber.py b/src/haven/instrument/ion_chamber.py
index 28b1dcea..09c57d95 100644
--- a/src/haven/instrument/ion_chamber.py
+++ b/src/haven/instrument/ion_chamber.py
@@ -597,15 +597,16 @@ async def load_ion_chamber(
         labels={"ion_chambers"},
     )
     # Ensure the voltmeter is in differential mode to measure pre-amp
-    try:
-        ion_chamber.voltmeter.differential.set(1).wait(timeout=1)
-    except OpException as exc:
-        msg = (
-            f"Could not set voltmeter {ion_chamber.name} channel differential state:"
-            f" {exc}"
-        )
-        log.warning(msg)
-        warnings.warn(msg)
+    if hasattr(ion_chamber, 'voltmeter'):
+        try:
+            ion_chamber.voltmeter.differential.set(1).wait(timeout=1)
+        except OpException as exc:
+            msg = (
+                f"Could not set voltmeter {ion_chamber.name} channel differential state:"
+                f" {exc}"
+            )
+            log.warning(msg)
+            warnings.warn(msg)
     return ion_chamber
 
 
diff --git a/src/haven/instrument/motor.py b/src/haven/instrument/motor.py
index 13d5d39b..3c674864 100644
--- a/src/haven/instrument/motor.py
+++ b/src/haven/instrument/motor.py
@@ -88,7 +88,6 @@ async def load_motor(prefix: str, motor_num: int, ioc_name: str = None):
     # Get the motor name from the description PV
     try:
         name = await caget(f"{pv}.DESC")
-        print(name)
     except asyncio.exceptions.TimeoutError:
         if not config["beamline"]["is_connected"]:
             # Beamline is not connected, so just use a generic name

From fc3c31921bad07cec506fe923691e4177f1b01c4 Mon Sep 17 00:00:00 2001
From: Mark Wolfman <canismarko@gmail.com>
Date: Thu, 16 Nov 2023 10:42:39 -0600
Subject: [PATCH 9/9] Fixed a broken test after the menu re-organization.

---
 src/firefly/tests/test_voltmeters.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/firefly/tests/test_voltmeters.py b/src/firefly/tests/test_voltmeters.py
index dff799ee..3763d044 100644
--- a/src/firefly/tests/test_voltmeters.py
+++ b/src/firefly/tests/test_voltmeters.py
@@ -64,8 +64,8 @@ def test_ion_chamber_menu(fake_ion_chambers, qtbot, ffapp):
     # Create the window
     window = FireflyMainWindow()
     # Check that the menu items have been created
-    assert hasattr(window.ui, "menuDetectors")
-    assert hasattr(window.ui, "menuIonChambers")
+    assert hasattr(window.ui, "detectors_menu")
+    assert hasattr(window.ui, "ion_chambers_menu")
     assert len(ffapp.ion_chamber_actions) == 2