The ObjNotifyPush module bridges MQTT real-time notifications with the Web Push API. It allows Axion to deliver browser push notifications to users who are offline or have the application in the background.
Push subscriptions are persisted in the def_notify_push_subscription database table and managed through explicit subscribe/unsubscribe calls from the browser client.
send_push().axion/{package}/push/{user} for real-time listeners (TUI, WebSocket dashboards).pywebpush for offline browser delivery.push event and calls showNotification().notificationclick and opens the target URL.Add to config.yaml under the base or package section:
base:
push:
vapid_private_key: <base64-encoded private key>
vapid_public_key: <base64-encoded public key>
vapid_email: mailto:admin@technocore.co.za
Generate VAPID keys with:
from py_vapid import Vapid
vapid = Vapid()
vapid.generate_keys()
print(vapid.private_pem())
print(vapid.public_key_urlsafe_base64())
| Column | Type | Description |
|---|---|---|
| User | char(255) | Axion username (PK) |
| Package | char(255) | Active package (PK) |
| Module | char(255) | Organisational field |
| Endpoint | varchar(2048) | Browser push endpoint URL (PK, indexed first 255 chars) |
| KeysP256dh | varchar(512) | P-256 Diffie-Hellman public key |
| KeysAuth | varchar(512) | Authentication secret |
| UserAgent | varchar(512) | Browser user agent string |
| CreatedAt | datetime | Subscription timestamp |
| Active | char(1) | Y = active, N = unsubscribed |
Primary key: (User, Package, Endpoint).
// Subscribe the browser to push notifications
axion_push_subscribe();
// Unsubscribe
axion_push_unsubscribe();
The service worker listens for push events from the Web Push API and displays native notifications. Clicking a notification opens the URL specified in the payload.
# Send a test push notification
python factory.core/ObjNotifyPush.py test-push admin --title "Alert" --body "Server check"
The Push channel can be added to ObjNotify.NOTIFY_TYPES.
When NotifyPushSend = 'Y' is set on a def_notify
record, deliver() delegates to
ObjNotifyPush.send_push() alongside other channels.
Keys are stored in config.yaml under base.push:
base:
push:
vapid_private_key: <keep secret>
vapid_public_key: <shared with browsers>
vapid_email: mailto:admin@technocore.co.za
Generate new keys:
python factory.core/ObjNotifyPush.py generate-vapid
| Package | Purpose |
|---|---|
pywebpush |
Send Web Push notifications |
py-vapid |
VAPID key generation |
paho-mqtt |
MQTT publish (already installed) |
29 tests across 12 classes:
| Class | Tests | Coverage |
|---|---|---|
| TestSubscribe | 5 | Valid, invalid JSON, missing endpoint/keys, DB error |
| TestUnsubscribe | 2 | Success, DB error |
| TestGetSubscriptions | 2 | Empty, returns list |
| TestSendPush | 3 | Success, no subs, webpush failure |
| TestSendPushToGroup | 2 | Group send, empty group |
| TestSendWebpush | 4 | No library, no VAPID, success, exception |
| TestSendMqttPush | 2 | Success, failure |
| TestVapidConfig | 2 | Load from config, missing config |
| TestMultipleSubscriptions | 2 | Multiple endpoints, send to all |
| TestPayloadFormat | 2 | Required fields, defaults |
| TestSubscriptionJson | 3 | Valid, empty, malformed |
pytest resource.test/pytests/factory.core/test_ObjNotifyPush.py -v
ObjNotify — notification routingObjMqtt — MQTT pub/subObjSms — SMS fallback for offline usersresource.web/service-worker.js — push event listenerresource.web/javascript/axion.push.js — client subscribe/unsubscribe