diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs
index abfaadcbe5..61a47e73c6 100644
--- a/Content.Client/IgnoredComponents.cs
+++ b/Content.Client/IgnoredComponents.cs
@@ -151,6 +151,7 @@ namespace Content.Client
"PressureVent",
"VolumePump",
"PressureSiphon",
+ "PipeHeater",
"SignalReceiver",
"SignalSwitch",
"SignalTransmitter",
diff --git a/Content.Server/GameObjects/Components/Atmos/Piping/PipeHeaterComponent.cs b/Content.Server/GameObjects/Components/Atmos/Piping/PipeHeaterComponent.cs
new file mode 100644
index 0000000000..234c9ac7cc
--- /dev/null
+++ b/Content.Server/GameObjects/Components/Atmos/Piping/PipeHeaterComponent.cs
@@ -0,0 +1,66 @@
+#nullable enable
+using Content.Server.GameObjects.Components.NodeContainer;
+using Content.Server.GameObjects.Components.NodeContainer.Nodes;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Log;
+using Robust.Shared.ViewVariables;
+using System.Linq;
+
+namespace Content.Server.GameObjects.Components.Atmos.Piping
+{
+ ///
+ /// Placeholder component for adjusting the temperature of gas in pipes.
+ ///
+ [RegisterComponent]
+ public class PipeHeaterComponent : Component
+ {
+ public override string Name => "PipeHeater";
+
+ [ViewVariables]
+ private PipeNode? _heaterPipe;
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ private float TargetTemperature { get; set; }
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ Owner.EnsureComponentWarn();
+ SetPipe();
+ }
+
+ public override void HandleMessage(ComponentMessage message, IComponent? component)
+ {
+ base.HandleMessage(message, component);
+ switch (message)
+ {
+ case PipeNetUpdateMessage:
+ Update();
+ break;
+ }
+ }
+
+ public void Update()
+ {
+ if (_heaterPipe == null)
+ return;
+
+ _heaterPipe.Air.Temperature = TargetTemperature;
+ }
+
+ private void SetPipe()
+ {
+ if (!Owner.TryGetComponent(out var container))
+ {
+ Logger.Error($"{nameof(PipeHeaterComponent)} on {Owner?.Prototype?.ID}, Uid {Owner?.Uid} did not have a {nameof(NodeContainerComponent)}.");
+ return;
+ }
+ _heaterPipe = container.Nodes.OfType().FirstOrDefault();
+ if (_heaterPipe == null)
+ {
+ Logger.Error($"{nameof(PipeHeaterComponent)} on {Owner?.Prototype?.ID}, Uid {Owner?.Uid} could not find compatible {nameof(PipeNode)}s on its {nameof(NodeContainerComponent)}.");
+ return;
+ }
+ }
+ }
+}
diff --git a/Resources/Prototypes/Entities/Constructible/Piping/heaters.yml b/Resources/Prototypes/Entities/Constructible/Piping/heaters.yml
new file mode 100644
index 0000000000..d71bff1d12
--- /dev/null
+++ b/Resources/Prototypes/Entities/Constructible/Piping/heaters.yml
@@ -0,0 +1,33 @@
+- type: entity
+ id: Heater
+ name: Heater
+ description: Heats gas.
+ placement:
+ mode: SnapgridCenter
+ components:
+ - type: Clickable
+ - type: InteractionOutline
+ - type: Physics
+ - type: SnapGrid
+ offset: Center
+ - type: Damageable
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 100
+ behaviors:
+ - !type:DoActsBehavior
+ acts: ["Destruction"]
+ - type: Sprite
+ netsync: false
+ sprite: Constructible/Atmos/heater.rsi
+ layers:
+ - state: heater
+ - state: heaterPipe
+ - type: NodeContainer
+ nodes:
+ - !type:PipeNode
+ nodeGroupID: Pipe
+ pipeDirection: East
+ - type: PipeHeater
\ No newline at end of file
diff --git a/Resources/Textures/Constructible/Atmos/heater.rsi/heater.png b/Resources/Textures/Constructible/Atmos/heater.rsi/heater.png
new file mode 100644
index 0000000000..3ed5ae46f9
Binary files /dev/null and b/Resources/Textures/Constructible/Atmos/heater.rsi/heater.png differ
diff --git a/Resources/Textures/Constructible/Atmos/heater.rsi/heaterOn.png b/Resources/Textures/Constructible/Atmos/heater.rsi/heaterOn.png
new file mode 100644
index 0000000000..597e2cc9cf
Binary files /dev/null and b/Resources/Textures/Constructible/Atmos/heater.rsi/heaterOn.png differ
diff --git a/Resources/Textures/Constructible/Atmos/heater.rsi/heaterPipe.png b/Resources/Textures/Constructible/Atmos/heater.rsi/heaterPipe.png
new file mode 100644
index 0000000000..620ba905be
Binary files /dev/null and b/Resources/Textures/Constructible/Atmos/heater.rsi/heaterPipe.png differ
diff --git a/Resources/Textures/Constructible/Atmos/heater.rsi/meta.json b/Resources/Textures/Constructible/Atmos/heater.rsi/meta.json
new file mode 100644
index 0000000000..3d46d2680e
--- /dev/null
+++ b/Resources/Textures/Constructible/Atmos/heater.rsi/meta.json
@@ -0,0 +1,21 @@
+{
+ "version":1,
+ "size":{
+ "x":32,
+ "y":32
+ },
+ "license":"CC-BY-SA-3.0",
+ "copyright":"Taken from https://github.com/tgstation/tgstation at commit 57cd1d59ca019dd0e7811ac451f295f818e573da",
+ "states":[
+ {
+ "name":"heater",
+ },
+ {
+ "name":"heaterOn",
+ },
+ {
+ "name":"heaterPipe",
+ "directions":4,
+ }
+ ]
+}
\ No newline at end of file