feat(notifications): custom data fields for Home Assistant notify services (#1441)

When an HA notification provider targets a notify service (e.g.
notify.mobile_app_myphone), a new optional Data (JSON) field is
forwarded as the service call's nested "data" object - the same
place HA automations put mobile push options like priority, ttl,
channel, and group. ttl: 0 + priority: high make Android pushes
arrive immediately; channel gives printer alerts their own sound.

JSON rather than key=value lines so numbers stay numbers and nested
options work. Validated on both ends: the UI rejects malformed JSON
before saving, and the sender fails loudly instead of posting a
half-built payload. Only included when configured - the default
persistent_notification.create path is unchanged, as its schema
rejects unknown keys.
This commit is contained in:
maziggy
2026-07-28 10:44:40 +02:00
parent d68724c689
commit 49f9d7120d
19 changed files with 242 additions and 14 deletions
@@ -144,6 +144,21 @@ export function AddNotificationModal({ provider, onClose }: AddNotificationModal
}
}
// HA custom service-data must be a JSON object (#1441)
if (providerType === 'homeassistant' && config.data?.trim()) {
let parsed: unknown;
try {
parsed = JSON.parse(config.data);
} catch {
setError(t('notifications.haDataInvalid'));
return;
}
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
setError(t('notifications.haDataInvalid'));
return;
}
}
const finalConfig: Record<string, unknown> =
providerType === 'ntfy' && Object.keys(eventPriorities).length > 0
? { ...config, event_priorities: eventPriorities }
@@ -265,6 +280,7 @@ export function AddNotificationModal({ provider, onClose }: AddNotificationModal
case 'homeassistant':
return [
{ key: 'service', label: 'Home Assistant Service', placeholder: 'notify.mobile_app_myphone', type: 'text', required: false },
{ key: 'data', label: 'Data (JSON, optional)', placeholder: '{"priority": "high", "ttl": 0, "channel": "3D Printing"}', type: 'textarea', required: false },
];
default:
return [];
@@ -368,6 +384,17 @@ export function AddNotificationModal({ provider, onClose }: AddNotificationModal
</option>
))}
</select>
) : field.type === 'textarea' ? (
<textarea
value={config[field.key] || ''}
onChange={(e) => {
setConfig({ ...config, [field.key]: e.target.value });
setTestResult(null);
}}
placeholder={field.placeholder}
rows={3}
className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none font-mono text-sm"
/>
) : (
<input
type={field.type}