diff --git a/pnpdevice/relays.py b/pnpdevice/relays.py
index 83fb4d8..7aee8a5 100644
--- a/pnpdevice/relays.py
+++ b/pnpdevice/relays.py
@@ -87,10 +87,10 @@ class Manager:
 		self.configpath = configpath
 		self.has_fakes = has_fakes
 
-		# Handles to various chips
-		self._chips = []
-		# Info on various pins
-		self._pins = []
+		# Handles to various chips, mapped by chip number
+		self._chips = {}
+		# Info on various pins, mapped by (chip number, line_number)
+		self._pins = {}
 		# Cached info on the relays
 		self._relays = []
 		# Connection to single-board computer GPIO
@@ -104,7 +104,7 @@ class Manager:
 	def chips(self) -> List[Chip]:
 		if not self._chips:
 			self._load_chips()
-		return self._chips
+		return self._chips.values()
 
 	def connect(self) -> None:
 		self.sbc = rgpio.sbc(
@@ -116,16 +116,18 @@ class Manager:
 			self.sbc = None
 
 	def get_chip_by_number(self, number: int) -> Chip:
-		for chip in self.chips:
-			if chip.number == number:
-				return chip
-		raise Exception(f"Can't find chip {number}")
+		try:
+			return self._chips[number]
+		except KeyError:
+			self._load_chip(number)
+			return self._chips[number]
 
 	def get_pin_by_number(self, chip: Chip, line_number: int) -> Pin:
-		for pin in self.pins:
-			if pin.chip == chip and pin.line_number == line_number:
-				return pin
-		raise Exception(f"Can't find pin {chip.name}-{line_number}")
+		try:
+			return self._pins[(chip.number, line_number)]
+		except KeyError:
+			self._load_pin(chip, line_number)
+			return self._pins[(chip.number, line_number)]
 
 	def get_relay_by_pin_id(self, pin_id: str) -> Relay:
 		for relay in self.relays:
@@ -133,11 +135,15 @@ class Manager:
 				return relay
 		return None
 
+	def load_all_pins(self) -> None:
+		self._load_chips()
+		self._load_pins()
+
 	@property
 	def pins(self) -> List[Pin]:
 		if not self._pins:
 			self._load_pins()
-		return self._pins
+		return self._pins.values()
 
 	@property
 	def relays(self) -> Iterable[Relay]:
@@ -177,50 +183,55 @@ class Manager:
 	def shutdown(self) -> None:
 		_write_config(self.configpath, self.config)
 
+	def _load_chip(self, number: int) -> None:
+		try:
+			handle = self.sbc.gpiochip_open(number)
+		except rgpio.error:
+			return
+		if handle < 0:
+			return
+		okay, number_of_lines, name, label = self.sbc.gpio_get_chip_info(handle)
+		LOGGER.info("Chip info: %s %s %s %s", okay, number_of_lines, name, label)
+		if okay != 0:
+			LOGGER.warn("Chip %s not okay.", name)
+			return
+		self._chips[number] = Chip(
+			handle=handle,
+			label=label,
+			name=name,
+			number=number,
+			number_of_lines=number_of_lines,
+		)
+
 	# Talk to the remote pins daemon and get information about the chips
 	def _load_chips(self) -> None:
 		for i in range(MAX_CHIPS):
-			try:
-				handle = self.sbc.gpiochip_open(i)
-			except rgpio.error:
-				continue
-			if handle < 0:
-				continue
-			okay, number_of_lines, name, label = self.sbc.gpio_get_chip_info(handle)
-			LOGGER.info("Chip info: %s %s %s %s", okay, number_of_lines, name, label)
-			if okay != 0:
-				LOGGER.warn("Chip %s not okay.", name)
-				continue
-			self._chips.append(Chip(
-				handle=handle,
-				label=label,
-				name=name,
-				number=i,
-				number_of_lines=number_of_lines,
-			))
+			self._load_chip(i)
+
+	def _load_pin(self, chip: Chip, line_number: int) -> None:
+		okay, offset, flags, name, user = self.sbc.gpio_get_line_info(chip.handle, line_number)
+		LOGGER.info("Got line info: %s %s %s %s %s", okay, offset, flags, name, user)
+		assert offset == line_number
+		if okay != 0:
+			LOGGER.warn("Line %s is not okay", name)
+			return
+		if not name:
+			LOGGER.warn("Ignoring line %d because it has no name.", line_number)
+			return
+		self._pins[(chip.number, line_number)] = Pin(
+			chip=chip,
+			flags=flags,
+			line_number=line_number,
+			name=name,
+			user=user,
+		)
 
 	# Talk to the remote pins daemon and get information about the pins
 	def _load_pins(self) -> None:
 		LOGGER.info("Loading pins")
 		for chip in self.chips:
 			for line in range(chip.number_of_lines):
-				okay, offset, flags, name, user = self.sbc.gpio_get_line_info(chip.handle, line)
-				LOGGER.info("Got line info: %s %s %s %s %s", okay, offset, flags, name, user)
-				assert offset == line
-				if okay != 0:
-					LOGGER.warn("Line %s is not okay", name)
-					continue
-				if not name:
-					LOGGER.warn("Ignoring line %d because it has no name.", line)
-					continue
-				self._pins.append(Pin(
-					chip=chip,
-					flags=flags,
-					line_number=line,
-					name=name,
-					user=user,
-				))
-
+				self._load_pin(chip, line)
 
 def _read_config(configpath: pathlib.Path) -> None:
 	with open(configpath, "rb") as f:
diff --git a/pnpdevice/server.py b/pnpdevice/server.py
index 22303e6..24b48d6 100644
--- a/pnpdevice/server.py
+++ b/pnpdevice/server.py
@@ -36,6 +36,7 @@ def index(request: Request):
 @app.get("/relay/create")
 def relay_create_get(request: Request):
 	"Get the form to create a new relay."
+	relays.load_all_pins()
 	pins = relays.pins
 	return templates.TemplateResponse("relay-create.template.html", {
 		"request": request,