A lightweight SSDP (Simple Service Discovery Protocol) implementation for Node.js with full TypeScript support.
npm install @gibme/ssdp
or
yarn add @gibme/ssdp
https://gibme-npm.github.io/ssdp
Discover SSDP services on the network with automatic periodic searching.
discover events for ssdp:alive, ssdp:update, and search replieswithdraw events for ssdp:byebye notificationsAnnounce SSDP services on the network with automatic periodic notifications.
M-SEARCH requests for:
upnp:rootdeviceuuid:<uuid>ssdp:allimport { Browser } from '@gibme/ssdp';
const browser = await Browser.create({
interval: 5_000,
services: ['urn:schemas-upnp-org:device:MediaServer:1']
});
browser.on('discover', (service, payload, remote, local) => {
console.log('Discovered:', { service, remote: remote.address });
});
browser.on('withdraw', (service, payload, remote, local) => {
console.log('Withdrawn:', { service, remote: remote.address });
});
// Trigger an immediate search
browser.searchNow();
// Subscribe to additional services dynamically
browser.subscribe('urn:schemas-upnp-org:device:InternetGatewayDevice:1');
// Unsubscribe from a service
browser.unsubscribe('urn:schemas-upnp-org:device:MediaServer:1');
// Clean up when done
browser.destroy();
import { Advertiser } from '@gibme/ssdp';
const advertiser = await Advertiser.create({
interval: 5_000,
services: {
'urn:schemas-upnp-org:device:MediaServer:1': {
'LOCATION': 'http://192.168.1.100:8080/description.xml'
}
}
});
// Announce a new service dynamically
advertiser.announce('urn:schemas-upnp-org:service:ContentDirectory:1', {
'LOCATION': 'http://192.168.1.100:8080/content.xml'
});
// Withdraw a specific service
advertiser.withdraw('urn:schemas-upnp-org:service:ContentDirectory:1');
// Trigger an immediate notification for all services
advertiser.announceNow();
// Clean up when done (sends ssdp:byebye for all services)
advertiser.destroy();
Control which hosts can discover your services:
import { Advertiser } from '@gibme/ssdp';
const allowedSubnets = ['192.168.1.', '10.0.0.'];
const advertiser = await Advertiser.create({
interval: 10_000,
services: {
'my:custom:service': {}
},
authenticationProvider: (ipAddress) => {
return allowedSubnets.some(subnet => ipAddress.startsWith(subnet));
}
});
For direct control over SSDP messaging:
import { SSDP } from '@gibme/ssdp';
const socket = await SSDP.create({ loopback: true, ttl: 2 });
socket.on('search', (payload, local, remote) => {
console.log('Search from:', remote.address, 'for:', payload.getHeader('ST'));
});
socket.on('notification', (payload, local, remote) => {
console.log('Notification:', payload.getHeader('NT'), payload.getHeader('NTS'));
});
socket.on('reply', (payload, local, remote) => {
console.log('Reply:', payload.getHeader('ST'));
});
// Send a search
await socket.search('ssdp:all');
// Send a notification
await socket.notify('my:service:type', { USN: 'uuid:my-device::my:service:type' });
// Send a byebye
await socket.bye('my:service:type', { USN: 'uuid:my-device::my:service:type' });
socket.destroy();
| Option | Type | Default | Description |
|---|---|---|---|
host |
string |
auto | Network interface to bind to |
loopback |
boolean |
false |
Whether to receive messages sent by this instance |
ttl |
number |
2 |
Multicast TTL (time-to-live) |
| Option | Type | Default | Description |
|---|---|---|---|
interval |
number |
60000 |
How often (ms) to search the network |
services |
string | string[] |
— | Service types to subscribe to |
| Option | Type | Default | Description |
|---|---|---|---|
interval |
number |
60000 |
How often (ms) to send notifications |
uuid |
string |
auto (v7) | UUID for this device instance |
services |
Record<string, HeadersInit> |
— | Services to advertise with their headers |
authenticationProvider |
(ip: string) => boolean | Promise<boolean> |
() => true |
Controls which hosts receive responses |
MIT