Skip to main content

Python framework for describing and visualizing wiring harnesses

Project description

loome

A Python tool for describing and visualizing aircraft wiring harnesses. Write a spec file in plain Python, then render schematic SVGs, bundle layout diagrams, bills of materials, and fuse schedules from it.

Installation

pip install loome

Quick start

# my_harness.py
from loome import Component, Connector, Fuse, GroundSymbol, Harness, Pin, SpliceNode


class PowerSupply(Component):
    class J1(Connector):
        positive = Pin(1, "12V Out")
        ground = Pin(2, "Ground")


class LoadDevice(Component):
    class J1(Connector):
        power = Pin(1, "Power In")
        ground = Pin(2, "Ground")
        signal = Pin(3, "Signal Out")


class Display(Component):
    class J1(Connector):
        power = Pin(1, "Power")
        ground = Pin(2, "Ground")
        data_in = Pin(3, "Data In")


psu = PowerSupply("Power Supply")
device = LoadDevice("Load Device")
display = Display("Display")

f1 = Fuse("F1", amps=5)
f2 = Fuse("F2", amps=3)
sp1 = SpliceNode("SP1", label="Power Splice")
gnd = GroundSymbol("GND")

psu.J1.positive >> f1
device.J1.power >> f1
display.J1.power >> f1

psu.J1.ground >> gnd
device.J1.ground >> gnd
display.J1.ground >> gnd

device.J1.signal >> display.J1.data_in

harness = Harness("My Harness", length_unit="in")
loome render my_harness.py -o my_harness.svg

Rendered output (examples/minimal.svg):

Minimal harness schematic


CLI reference

Every subcommand takes a spec file as its first positional argument. The spec must assign a Harness instance to a module-level variable named harness.

loome render <spec>

Renders a schematic SVG showing all components, connectors, pins, and wire connections.

Flag Default Description
-o / --output out.svg Output SVG path
--no-color off Render wires in monochrome
--show-unconnected off Include unconnected pins

loome bundle <spec>

Renders the physical bundle topology as SVG — the tree of breakout nodes, trunk lengths, and connector stubs.

Flag Default Description
-o / --output bundle.svg Output SVG path (stem is reused per bundle)
--name all Render only the named bundle

loome bom <spec>

Emits a bill of materials: wires (by gauge and color), connectors, and terminals.

Flag Default Description
-o / --output stdout Output file
--format md md or csv

loome fuses <spec>

Emits a fuse/circuit-breaker schedule listing what each protective device feeds.

Flag Default Description
-o / --output stdout Output file
--format md md or csv

Defining components

A component is a Python class that subclasses Component. Connectors are inner classes subclassing Connector, and pins are class attributes created with Pin(number, signal_name).

from loome import Component, Connector, Pin

class MyECU(Component):
    class J1(Connector):
        power  = Pin(1, "Power In")
        ground = Pin(2, "Ground")

    class J2(Connector):
        rs232_tx = Pin(3, "RS-232 TX")
        rs232_rx = Pin(4, "RS-232 RX")

Pin numbers can be integers or strings (useful for wire-color-coded devices like OAT probes):

class OATProbe(Component):
    power = Pin("WHT", "OAT Probe Power")
    sense = Pin("BLU", "OAT Probe Sense")
    low   = Pin("ORN", "OAT Probe Low")

Components are instantiated with a display label:

ecu   = MyECU("Engine ECU")
probe = OATProbe("OAT Probe")

Composite port types

Pre-built port descriptors handle common multi-wire interfaces and auto-wire shared bus lines:

from loome import Component, Connector, Pin, CanBus, RS232, GPIO

class Sensor(Component):
    class J1(Connector):
        can    = CanBus(1, 2)                        # CAN High pin 1, Low pin 2
        serial = RS232(5, 4, 6, name="RS-232 1")     # TX, RX, GND
        pos    = GPIO(7, 8, 9, name="Position")      # +, signal, GND (shielded)

RS232 instances cross-wire automatically: device_a.J1.serial.connect(device_b.J1.serial).

Switches (built-in)

from loome import SPST, SPDT, DPST, DPDT

sw = SPST("Master Switch")           # pins: com, no
sw = SPDT("Fuel Selector")           # pins: com, no, nc
sw = DPST("Dual Master", momentary=True)  # pins: com1, no1, com2, no2
sw = DPDT("Crossfeed")               # pins: com1, no1, nc1, com2, no2, nc2

Configuration-dependent wiring

Override __init__ to apply wiring that depends on instantiation arguments:

from loome.constants import Axis

class Servo(Component):
    def __init__(self, name: str, axis: Axis | None = None):
        super().__init__(name)
        if axis == Axis.PITCH:
            self.J1[5].connect(self.J1[8])   # strap ID pins

    class J1(Connector):
        id_strap_1 = Pin(5, "ID Strap 1")
        id_strap_4 = Pin(8, "ID Strap 4")

pitch_servo = Servo("Pitch Servo", axis=Axis.PITCH)

Defining a harness

A harness spec is a plain .py file. The CLI exec()s it and looks for a module-level variable named harness.

Wiring

Wires connect pins and terminals using >> (operator) or .connect() (method):

# Operator style — returns a WireBuilder for optional chaining
pin_a >> pin_b
(pin_a >> pin_b).gauge(20).color("R").wire_id("W1").notes("check polarity")

# Method style — returns a WireSegment directly
pin_a.connect(pin_b, wire_id="W1", gauge=22, color="R")

Wire colors: "R" red, "W" white, "B" / "N" black, "BL" blue, "OR" orange, "Y" yellow, "GN" green, "GR" gray, "PK" pink, "VT" violet, "" auto.

Default gauge is 22 AWG.

Terminals

Terminals are the endpoints for wires that don't go to another connector pin.

from loome import GroundSymbol, OffPageReference, Fuse, CircuitBreaker, BusBar

gnd     = GroundSymbol("GND")                  # chassis ground (filled triangle)
local   = GroundSymbol("local", filled=False)  # signal/local ground (open triangle)
ext_ref = OffPageReference("EXT1", label="To Avionics Bus")  # off-page chevron
fuse    = Fuse("F1", amps=5)
cb      = CircuitBreaker("CB1", amps=10)
bus     = BusBar("BATT_BUS", label="Battery Bus")

Terminals are wire endpoints — connect to them from a pin or splice node:

pin >> gnd
pin >> fuse
splice >> pin

Splice nodes

A splice node fans one wire out to many destinations:

from loome import SpliceNode

sp = SpliceNode("SP1", label="Power Splice")
source_pin >> fuse
sp >> load_a.J1.power
sp >> load_b.J1.power
sp >> load_c.J1.power

Fuse blocks

from loome import Fuse, FuseBlock

# Declarative subclass (preferred for named blocks)
class AvionicsBlock(FuseBlock):
    PFD   = Fuse("PFD",   amps=5)
    MFD   = Fuse("MFD",   amps=5)
    COMM1 = Fuse("COMM1", amps=7.5)

fb = AvionicsBlock()
bus >> fb.PFD   # connect bus to each fuse

Shields

from loome import Shield

with Shield(drain=gnd, drain_remote=local) as oat_shield:
    sensor.J1.high.connect(ecu.J2.sense_high)
    sensor.J1.low.connect(ecu.J2.sense_low)

All connections inside the with block share one shield foil.

CAN bus lines

from loome import CanBusLine

CanBusLine(
    name="CAN Bus",
    devices=[device_a.J1, device_b.J1, device_c.J1],  # ordered along bus
)

Physical bundle topology

Bundles let loome compute wire lengths from a topology tree of breakout points:

from loome import Bundle

main      = Bundle("Main Run")
firewall  = main.breakout("firewall")
mid_cabin = main.breakout("mid-cabin", after=firewall, length=36)  # 36 in trunk
tail      = main.breakout("tail",      after=mid_cabin, length=24)

firewall.attach(ecu.J1,    leg_length=4)   # 4 in stub from trunk to connector
firewall.attach(gnd,       leg_length=2)
mid_cabin.attach(device.J1, leg_length=6)
tail.attach(sensor.J1,    leg_length=3)

Harness object

harness = Harness("My Harness", length_unit="in")

Harness auto-detects all components, splices, terminals, shields, bundles, and CAN bus lines defined in the spec's module namespace — no need to register them manually.


Built-in component library

Loome ships Garmin avionics definitions under loome.components:

from loome.components.garmin import (
    GAD27, GAD29C, GDU460, GEA24, GMC507, GMU11,
    GSA28, GSU25, GTR20, GTX45R, GDL51R, GMA245, GTP59, G5, GAP2620,
)
from loome.components.gtn650 import GTN650Xi
from loome.components import RayAllanTrim, Stick, LEMO, TRS

Imports reference

Everything needed for a spec file is importable from the top-level loome package:

from loome import (
    # Core
    Component, Connector, Pin, WireColor,
    # Terminals
    GroundSymbol, OffPageReference, Fuse, FuseBlock,
    CircuitBreaker, CircuitBreakerBank, BusBar, SpliceNode, Terminal,
    # Composite ports
    CanBus, RS232, GPIO,
    # Shields
    Shield, ShieldGroup,
    # Topology
    Bundle, Breakout, CanBusLine,
    # Harness
    Harness,
    # Switches
    SPST, SPDT, DPST, DPDT,
)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

loome-0.2.0.tar.gz (101.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

loome-0.2.0-py3-none-any.whl (76.2 kB view details)

Uploaded Python 3

File details

Details for the file loome-0.2.0.tar.gz.

File metadata

  • Download URL: loome-0.2.0.tar.gz
  • Upload date:
  • Size: 101.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for loome-0.2.0.tar.gz
Algorithm Hash digest
SHA256 daf3176472e0c9857c9e8200bfcff20a5c861777e285bced1250725c724c97c1
MD5 82141ea0c670658d032f9defb9141f29
BLAKE2b-256 e0d7efe88b0398cc5de05b5d08861ca92bfd07fb8d7e87a930633ad02f7b557a

See more details on using hashes here.

Provenance

The following attestation bundles were made for loome-0.2.0.tar.gz:

Publisher: python-publish.yml on erik-hasse/loome

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file loome-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: loome-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 76.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for loome-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7c4fc3cf5c769abf4d9684cec3173ecb74dd43beb32e5670268334502a867b89
MD5 6008b4f35166627cf659f9b2714ca31a
BLAKE2b-256 427bc5ef0aeca5846f5b9063dba8b76ceb6490b8cb40a8903071848a60162f80

See more details on using hashes here.

Provenance

The following attestation bundles were made for loome-0.2.0-py3-none-any.whl:

Publisher: python-publish.yml on erik-hasse/loome

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page