nodes: what porting the house needed from the vocabulary
Docs / docs (push) Canceled after 0s
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Docs / docs (push) Canceled after 0s
Playwright Tests / test-playwright (1, 2) (push) Canceled after 0s
Playwright Tests / test-playwright (2, 2) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Test Backend / test-backend (push) Canceled after 0s
Compose Smoke Test / test-compose (push) Canceled after 0s
Playwright Tests / merge-reports (push) Canceled after 0s
Four small things, each with a device behind it.
An MQTT filter now routes what it subscribed to. `+` and `#` reached the
broker and were then looked up in an exact-match dict, so every message a
wildcard subscription received was dropped in silence.
`json_key` lifts a value out of the object a device wraps it in — Victron
publishes `{"value": 47}` on every path, which was otherwise a Python node
per port.
The trigger node learned `passthrough` and `wait_port`, because how long to
wait can be a value rather than a constant: a rollershutter takes 26 seconds
up and 28 down. A wait of zero sends nothing afterwards and still cancels
what the last message scheduled, which is how a stop is commanded once
instead of forever.
The HTTP sender takes fixed `query` parameters, so an API key is a secret
reference rather than a message on the canvas, and `send_inputs` off for a
request whose inputs are only a trigger.
Also: `delay` accepts fractional seconds, and `TZ` reaches the container, so
a cron expression means local time. Left unset it is UTC, as before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -130,6 +130,8 @@ class HttpNode(Node):
|
||||
"mode",
|
||||
"timeout",
|
||||
"headers",
|
||||
"query",
|
||||
"send_inputs",
|
||||
"secret",
|
||||
"_route_registered",
|
||||
)
|
||||
@@ -141,6 +143,21 @@ class HttpNode(Node):
|
||||
method: Literal["GET", "POST"] = "POST"
|
||||
timeout: float = 30.0
|
||||
headers: dict[str, str] = {}
|
||||
query: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description=(
|
||||
"Fixed query parameters, sender mode. A value may be a secret "
|
||||
"reference, which is how an API key stays out of the flow file."
|
||||
),
|
||||
)
|
||||
send_inputs: bool = Field(
|
||||
default=True,
|
||||
description=(
|
||||
"Send the node's inputs as the request body or query. Off when "
|
||||
"the inputs are only a trigger and the request is fully "
|
||||
"described by 'url' and 'query'."
|
||||
),
|
||||
)
|
||||
secret: str = Field(
|
||||
default="",
|
||||
description=(
|
||||
@@ -197,6 +214,8 @@ class HttpNode(Node):
|
||||
self.method = cfg.method.upper()
|
||||
self.timeout = cfg.timeout
|
||||
self.headers = cfg.headers
|
||||
self.query = cfg.query
|
||||
self.send_inputs = cfg.send_inputs
|
||||
self.secret = cfg.secret
|
||||
self._route_registered = False
|
||||
|
||||
@@ -251,18 +270,20 @@ class HttpNode(Node):
|
||||
:rtype: dict | None
|
||||
"""
|
||||
client = shared_client()
|
||||
payload = dict(kwargs) if self.send_inputs else {}
|
||||
try:
|
||||
if self.method == "GET":
|
||||
response = client.get(
|
||||
self.url,
|
||||
params=kwargs,
|
||||
params={**self.query, **payload},
|
||||
headers=self.headers,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
else: # POST
|
||||
response = client.post(
|
||||
self.url,
|
||||
json=kwargs,
|
||||
params=self.query or None,
|
||||
json=payload,
|
||||
headers=self.headers,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user