diff --git a/src/Scripting/NasalSys.hxx b/src/Scripting/NasalSys.hxx
index ed796522c..7868882e9 100644
--- a/src/Scripting/NasalSys.hxx
+++ b/src/Scripting/NasalSys.hxx
@@ -156,6 +156,13 @@ public:
 
     string_list getAndClearErrorList();
 
+    /**
+     @brief Convert the value of an SGPropertyNode to its Nasal representation. Used by
+     props.Node.getValue internally, but exposed here for other use cases which don't want to create
+     a props.Node wrapper each time.
+     */
+    static naRef getPropertyValue(naContext c, SGPropertyNode* node);
+
 private:
     void initLogLevelConstants();
 
diff --git a/src/Scripting/nasal-props.cxx b/src/Scripting/nasal-props.cxx
index 592a9e80f..bcfced050 100644
--- a/src/Scripting/nasal-props.cxx
+++ b/src/Scripting/nasal-props.cxx
@@ -305,30 +305,7 @@ static naRef f_getValue(naContext c, naRef me, int argc, naRef* args)
     using namespace simgear;
     NODEARG();
     MOVETARGET(naVec_size(argv) > 0, false);
-    switch(node->getType()) {
-    case props::BOOL:   case props::INT:
-    case props::LONG:   case props::FLOAT:
-    case props::DOUBLE:
-    {
-        double dv = node->getDoubleValue();
-        if (SGMisc<double>::isNaN(dv)) {
-          SG_LOG(SG_NASAL, SG_ALERT, "Nasal getValue: property " << node->getPath() << " is NaN");
-          return naNil();
-        }
-
-        return naNum(dv);
-    }
-
-    case props::STRING:
-    case props::UNSPECIFIED:
-        return NASTR(node->getStringValue().c_str());
-    case props::VEC3D:
-        return makeVectorFromVec(c, node->getValue<SGVec3d>());
-    case props::VEC4D:
-        return makeVectorFromVec(c, node->getValue<SGVec4d>());
-    default:
-        return naNil();
-    }
+    return FGNasalSys::getPropertyValue(c, node);
 }
 
 template<typename T>
@@ -941,3 +918,35 @@ naRef FGNasalSys::genPropsModule()
                 naNewFunc(_context, naNewCCode(_context, propfuncs[i].func)));
     return namespc;
 }
+
+naRef FGNasalSys::getPropertyValue(naContext c, SGPropertyNode* node)
+{
+    using namespace simgear;
+    if (!node)
+        return naNil();
+    
+    switch(node->getType()) {
+    case props::BOOL:   case props::INT:
+    case props::LONG:   case props::FLOAT:
+    case props::DOUBLE:
+    {
+        double dv = node->getDoubleValue();
+        if (SGMisc<double>::isNaN(dv)) {
+          SG_LOG(SG_NASAL, SG_ALERT, "Nasal getValue: property " << node->getPath() << " is NaN");
+          return naNil();
+        }
+
+        return naNum(dv);
+    }
+
+    case props::STRING:
+    case props::UNSPECIFIED:
+        return NASTR(node->getStringValue().c_str());
+    case props::VEC3D:
+        return makeVectorFromVec(c, node->getValue<SGVec3d>());
+    case props::VEC4D:
+        return makeVectorFromVec(c, node->getValue<SGVec4d>());
+    default:
+        return naNil();
+    }
+}