{% from "_serial_port_select.html" import device_field %} {% macro serial_fields(s, prefix, ports) %} {{ device_field(s.port, prefix, ports, not remote, links) }} {% if 'usb_match' not in hide %} {% endif %}
{% if 'port_lock' not in hide %}
{{ _("Keeping other programs off the port (Linux, macOS)") }}

{{ _("“Lock the port” (flock) keeps out other ser2netweb mappings and programs that lock a port the same way (picocom, tio, miniterm), but not minicom, programs that use no lock, a login prompt or ModemManager. On Windows a port is always open in one program only, and these options do nothing.") }}

{% endif %}
{{ _("RS-485 (half-duplex, hardware auto-RTS)") }}
{% endmacro %} {% set ip_values = ips | map(attribute='value') | list %} {% set bind_choice = '*' if mapping.network.both_families else mapping.network.bind_ip %} {% set bind_is_custom = bind_choice not in ip_values %}
{% if remote %} {% endif %} {% if not is_new %}{% endif %} {% if rev and not is_new %}{% endif %}

{{ _("New mapping") if is_new else _("Edit mapping") }}{% if remote %} {{ _("on %(host)s", host=remote.addr) }}{% endif %}

{% if remote and remote.version_note %}{% endif %} {% if error %}{% endif %} {% for note in mapping.ignored_settings() %}{% endfor %}
{{ _("Serial port") }}{% if mapping.kind == 'serialbridge' %} A{% endif %} {{ serial_fields(mapping.serial, "serial_", ports) }}
{{ _("Serial port") }} B {{ serial_fields(mapping.serial_b, "serial_b_", ports) }}
{{ _("Network") }}
{% if 'dual_stack' not in hide %}{% endif %}
{% if 'failover' not in hide %}
{% endif %}
{% if 'open_on_connect' not in hide %}
{% endif %} {% if 'pool' not in hide %} {% endif %} {% if mapping.network.kick_old_user and not mapping.network.allowed_client_ips %}

{{ _("“Kick old client” is on and no allowed client IPs are set: any host that can reach this port can take the connection over from the client using it.") }}

{% endif %}
{% if 'writer_ips' not in hide %} {% endif %}
{% if 'udp_reply' not in hide %}
{% if 'udp_targets' not in hide %}
{% endif %}

{{ _("The device’s output goes to the sender of the last datagram from an allowed IP, so any of them can take it over; with a reply address it always goes there. The allow-list checks a datagram’s source address, which can be forged: on an untrusted network use TCP with TLS, or a firewall.") }}

{% endif %}
{% if 'tls_checks' not in hide %}

{{ _("Without a CA file or a fingerprint the link is encrypted, but the server’s certificate is not checked: anyone on the network path could pose as the server. The mapping’s log shows the fingerprint of the certificate at each connection.") }}

{% endif %} {% if not remote and request.state.user and request.state.user.role == 'admin' %}
{{ _("Upload TLS files from this computer…") }}

{{ _('PEM files, up to %(kb)s KB each. They are checked (the key must be the certificate’s; an encrypted key needs its password in the field below), kept in the data folder’s tls/ and their paths are put into the fields above: save the form to use them.', kb=tls_upload_kb) }}

{% if 'tls_checks' not in hide %} {% endif %}
{% endif %} {% if 'tls_key_password' not in hide %} {% set tls_pw_kept = tls_pw_stored if tls_pw_stored is defined else mapping.network.tls_key_password %}
{% if tls_pw_kept %}{% endif %}
{% endif %}
{% if 'disconnect_without_device' not in hide %}
{% endif %}
{{ _("Options") }} {%- set hidden_chars = _("It holds control characters (NUL, CR or LF) that this box can’t show; they are kept unless you change the text.") %}
{% if 'modbus_gateway' not in hide %}
{% endif %} {% if 'rfc2217_restore' not in hide %}
{% if 'rfc2217_signature' not in hide %} {% endif %}
{% endif %} {% if 'packing' not in hide %}
{% endif %} {% if 'poll_string' not in hide %}
{% endif %}
{% if 'no_data_alarm' not in hide %}
{% endif %}

{{ _("Traces hold the raw traffic in both directions, personal data and passwords typed to the device included: delete them once they are no longer needed.") }}

{% if 'trace_rotation' not in hide %}
{% endif %} {% if 'console_encoding' not in hide %}
{% endif %}
{{ _("MQTT publishing") }} ({{ _("serial lines → broker; needs paho-mqtt") }})({{ _("Modbus points → broker, commands → writable points; needs paho-mqtt") }})
{% if 'mqtt5' not in hide %}
{% endif %} {% if 'mqtt_retain' not in hide %}
{% endif %}
{# write-only: the stored password is never in the page; blank keeps it #} {% set pw_stored = mqtt_password_stored | default(false) %}
{% if pw_stored %}{% endif %} {% if 'serial_mqtt' not in hide %}
{% if mapping.accepts_mqtt_tx and not (mapping.mqtt.tls and mapping.mqtt.username) %}{% endif %}
{% endif %} {% if 'mqtt_tls_files' not in hide %}
{# write-only, as the TLS key password: the stored one is never in the page; blank keeps it #} {% set mqtt_key_pw_kept = mqtt_key_pw_stored if mqtt_key_pw_stored is defined else mapping.mqtt.client_key_password %}
{% if mqtt_key_pw_kept %}{% endif %}
{% endif %} {% if 'payload_format' not in hide %}
{% if 'primary_host' not in hide %}
{% endif %} {% endif %}
{{ _("Modbus register polling → MQTT") }} ({{ _("edge mode; also enable MQTT above") }})

{{ _('Every topic, payload and write, and the Sparkplug B model: docs/MODBUS-MQTT.md.') }}

{% if 'block_reads' not in hide %}
{% endif %} {% if 'point_options' not in hide %}
{% endif %} {% if not is_new and not remote %}
{{ _("through the running gateway, as typed above; nothing is saved or published") }}
{% endif %}

{{ _('Who may write is up to the broker: let only trusted clients publish to <topic>/+/set (Sparkplug: the node’s NCMD and DCMD topics), and use TLS and a username. Read-only refuses every write.') }}

{% if mapping.modbus_poll.broadcast_points %}{% endif %} {% if mapping.modbus_poll.reserved_unit_points %}{% endif %} {% if mapping.accepts_mqtt_writes and not (mapping.mqtt.tls and mapping.mqtt.username) %}{% endif %} {% if 'retained_set' not in hide %}
{% if 'value_format' not in hide %} {% endif %} {% if 'unit_status' not in hide %} {% endif %}
{% endif %}
{% if not remote and request.state.user and request.state.user.role == 'admin' %} {# the upload's own form: its file inputs sit in the form above (form="tls-upload") and are no part of that one #}
{% endif %}