Skip to content
This repository has been archived by the owner on Dec 4, 2020. It is now read-only.

Commit

Permalink
WIP: Stop discovery (#169)
Browse files Browse the repository at this point in the history
### Added
- [#2](#2) - Calling `Discovery.stop()` stops listening for SSDP advertisements

### Changed
- Replaced `null` with `undefined` according to [TypeScript guidelines](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined)
  • Loading branch information
FantasticFiasco authored Jun 24, 2017
1 parent 585353c commit c3bc05a
Show file tree
Hide file tree
Showing 22 changed files with 281 additions and 303 deletions.
3 changes: 0 additions & 3 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ src
test
.gitattributes

## Demo application
demo-application

## Visual Studio Code settings
.vscode

Expand Down
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

## [Unreleased]

### Added

- [#2](https://github.com/FantasticFiasco/axis-discovery-ssdp/issues/2) - Calling `Discovery.stop()` stops listening for SSDP advertisements

### Changed

- Replaced `null` with `undefined` according to [TypeScript guidelines](https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined)

## 1.0.2 - 2017-01-26

### Fixed
- [#40](https://github.com/FantasticFiasco/axis-discovery-ssdp/issues/40) - Fixed issue where calling `Discovery.search` didn't trigger a new search

- [#40](https://github.com/FantasticFiasco/axis-discovery-ssdp/issues/40) - Calling `Discovery.search()` didn't trigger a new search

## 1.0.1 - 2016-12-06

### Fixed

- Updated `README.md` in package

## 1.0.0 - 2016-12-04

### Added

- Support for discovering [Axis Communications](http://www.axis.com/) devices on the network using SSDP
27 changes: 9 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ A Node.js SSDP client library written in TypeScript capable of searching for [Ax
## Table of contents

- [Super simple to use](#super-simple-to-use)
- [Features](#features)
- [Installation](#installation)
- [API](#api)
- [Credit](#credit)
Expand All @@ -21,8 +20,6 @@ A Node.js SSDP client library written in TypeScript capable of searching for [Ax

## Super simple to use

The following code is from the [demo application](https://github.com/FantasticFiasco/axis-discovery-ssdp/tree/master/demo-application).

```javascript
import * as ssdp from 'axis-discovery-ssdp';

Expand All @@ -36,16 +33,10 @@ discovery.onGoodbye((device: ssdp.Device) => {
console.log(`Goodbye from ${device.address}`);
});

discovery.start();
await discovery.start();
await discovery.search();
```

## Features

- Supports passively listening for SSDP announcements from cameras
- Supports actively searching for cameras using M-SEARCH
- Supports discovering cameras on multiple network interfaces
- TypeScript declarations are bundled together with the package

## Installation

```sh
Expand Down Expand Up @@ -97,37 +88,37 @@ class Device {
/**
* The port.
*/
readonly port: number | null;
readonly port: number | undefined;

/**
* The serial number.
*/
readonly serialNumber: string | null;
readonly serialNumber: string | undefined;

/**
* The short description for the end user.
*/
readonly friendlyName: string | null;
readonly friendlyName: string | undefined;

/**
* The model name.
*/
readonly modelName: string | null;
readonly modelName: string | undefined;

/**
* The long model description for the end user.
*/
readonly modelDescription: string | null;
readonly modelDescription: string | undefined;

/**
* The model number.
*/
readonly modelNumber: string | null;
readonly modelNumber: string | undefined;

/**
* The URL to presentation for device.
*/
readonly presentationURL: string | null;
readonly presentationURL: string | undefined;
}
```

Expand Down
1 change: 0 additions & 1 deletion demo-application/.gitignore

This file was deleted.

13 changes: 0 additions & 13 deletions demo-application/index.ts

This file was deleted.

31 changes: 0 additions & 31 deletions demo-application/package.json

This file was deleted.

11 changes: 0 additions & 11 deletions demo-application/tsconfig.json

This file was deleted.

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,17 @@
"pack": "npm pack"
},
"dependencies": {
"bluebird": "3.5.0",
"@fantasticfiasco/expect": "1.0.0",
"lodash": "4.17.4",
"request": "2.81.0",
"request-promise": "4.2.1",
"xml2js": "0.4.17"
},
"devDependencies": {
"@types/bluebird": "3.5.7",
"@types/chai": "4.0.0",
"@types/lodash": "4.14.65",
"@types/mocha": "2.2.41",
"@types/node": "8.0.2",
"@types/request": "0.0.44",
"@types/request-promise": "4.1.35",
"@types/sinon": "2.3.1",
"@types/xml2js": "0.4.0",
"chai": "4.0.2",
Expand Down
14 changes: 7 additions & 7 deletions src/Device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,30 @@ export class Device {
/**
* The port.
*/
readonly port: number | null,
readonly port: number | undefined,
/**
* The serial number.
*/
readonly serialNumber: string | null,
readonly serialNumber: string | undefined,
/**
* The short description for the end user.
*/
readonly friendlyName: string | null,
readonly friendlyName: string | undefined,
/**
* The model name.
*/
readonly modelName: string | null,
readonly modelName: string | undefined,
/**
* The long model description for the end user.
*/
readonly modelDescription: string | null,
readonly modelDescription: string | undefined,
/**
* The model number.
*/
readonly modelNumber: string | null,
readonly modelNumber: string | undefined,
/**
* The URL to presentation for device.
*/
readonly presentationURL: string | null) {
readonly presentationURL: string | undefined) {
}
}
45 changes: 18 additions & 27 deletions src/DeviceMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,47 +23,38 @@ export class DeviceMapper {

return new Device(
message.remoteAddress,
null,
undefined,
serialNumber,
null,
null,
null,
null,
null);
undefined,
undefined,
undefined,
undefined,
undefined);
}

/**
* Maps a root description to a device.
*/
public async fromRootDescriptionAsync(rootDescription: RootDescription): Promise<Device> {
const serialNumber = await rootDescription.getSerialNumberAsync();
const friendlyName = await rootDescription.getFriendlyNameAsync();
const modelName = await rootDescription.getModelNameAsync();
const modelDescription = await rootDescription.getModelDescriptionAsync();
const modelNumber = await rootDescription.getModelNumberAsync();
const presentationUrl = await rootDescription.getPresentationUrlAsync();

const port = this.parsePortFromPresentationUrl(presentationUrl);

public fromRootDescription(rootDescription: RootDescription): Device {
return new Device(
rootDescription.remoteAddress,
port,
serialNumber,
friendlyName,
modelName,
modelDescription,
modelNumber,
presentationUrl);
this.parsePortFromPresentationUrl(rootDescription.presentationUrl),
rootDescription.serialNumber,
rootDescription.friendlyName,
rootDescription.modelName,
rootDescription.modelDescription,
rootDescription.modelNumber,
rootDescription.presentationUrl);
}

private parsePortFromPresentationUrl(presentationUrl: string | null): number | null {
if (presentationUrl === null) {
return null;
private parsePortFromPresentationUrl(presentationUrl: string | undefined): number | undefined {
if (presentationUrl === undefined) {
return undefined;
}

const portMatch = DeviceMapper.portFromPresentationUrlRegExp.exec(presentationUrl);
if (portMatch == null) {
return null;
return undefined;
}

return Number(portMatch[1]);
Expand Down
44 changes: 29 additions & 15 deletions src/Discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,39 @@ export class Discovery {
/**
* Start listen for SSDP advertisements on all network interface addresses.
*/
public start() {
public async start(): Promise<void> {
const addresses = this.networkInterfaceMonitor.getIPv4Addresses();

// Start passive SSDP
this.startSocket(new NotifySocket(addresses));
await this.startSocket(new NotifySocket(addresses));

// Start active SSDP
_.forEach(addresses, (address) => this.startSocket(new MSearchSocket(address)));
for (const address of addresses) {
await this.startSocket(new MSearchSocket(address));
}
}

/**
* Stop listening for SSDP advertisements.
*/
public async stop(): Promise<void> {
for (const socket of this.sockets.splice(0, this.sockets.length)) {
await socket.stop();
}
}

/**
* Triggers a new SSDP search for devices on the network.
*/
public search() {
_.chain(this.sockets)
public async search(): Promise<void> {
const mSearchSockets = _.chain(this.sockets)
.filter((socket) => socket instanceof MSearchSocket)
.map((socket) => socket as MSearchSocket)
.value()
.forEach((socket) => socket.search());
.value();

for (const mSearchSocket of mSearchSockets) {
await mSearchSocket.search();
}
}

/**
Expand All @@ -57,33 +71,33 @@ export class Discovery {
this.eventEmitter.on('goodbye', (device: Device) => callback(device));
}

private startSocket(socket: SocketBase) {
private async startSocket(socket: SocketBase): Promise<void> {
this.sockets.push(socket);
socket.on('hello', (message: Message) => this.onHelloMessage(message));
socket.on('goodbye', (message: Message) => this.onGoodbyeMessage(message));
socket.start();
await socket.start();
}

private onHelloMessage(message: Message) {
// Emit initial hello
this.eventEmitter.emit('hello', this.deviceMapper.fromMessage(message));

// Request root description
this.requestRootDescriptionAsync(message.remoteAddress, message.location);
this.requestRootDescription(message.remoteAddress, message.location);
}

private onGoodbyeMessage(message: Message) {
this.eventEmitter.emit('goodbye', this.deviceMapper.fromMessage(message));
}

private async requestRootDescriptionAsync(remoteAddress: string, location: string): Promise<void> {
private async requestRootDescription(remoteAddress: string, location: string): Promise<void> {
try {
const request = new RootDescriptionRequest(remoteAddress, location);
const rootDescription = await request.sendAsync();
const device = await this.deviceMapper.fromRootDescriptionAsync(rootDescription);
const rootDescription = await request.send();
const device = this.deviceMapper.fromRootDescription(rootDescription);
this.eventEmitter.emit('hello', device);
} catch (e) {
Log.write(`Unable to get root description. ${e}`);
} catch (error) {
Log.write(`Unable to get root description. ${error}`);
}
}
}
Loading

0 comments on commit c3bc05a

Please sign in to comment.