Skip to content

Commit

Permalink
feat: add hubdevices endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
rossiam committed Jun 1, 2022
1 parent e32fb79 commit 1bcdf62
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 0 deletions.
98 changes: 98 additions & 0 deletions src/endpoint/hubdevices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Endpoint } from '../endpoint'
import { EndpointClient, EndpointClientConfig } from '../endpoint-client'


export interface EnrolledChannel {
channelId: string
name: string
description?: string

/**
* ISO-8601 timestamp of creation of channel.
*/
createdDate?: string

/**
* ISO-8601 timestamp of last modification of channel
*/
lastModifiedDate?: string

/**
* URL to web interface to modify channel subscriptions.
*/
subscriptionUrl?: string
}

export interface InstalledDriver {
driverId: string
name: string
description?: string
version: string
channelId: string
developer: string

/**
* Information on how to reach the vendor.
*/
vendorSupportInformation: string

/**
* map of permissions and attributes used by the driver.
*
* Format:
* {"permissions":{"perm1":{...}, "perm2"{...}}}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
permissions: { [name: string]: any }
}

export class HubdevicesEndpoint extends Endpoint {
constructor(config: EndpointClientConfig) {
super(new EndpointClient('hubdevices', config))
}

/**
* Install driver on a hub. The primary use case of this functionality is to install a self
* published driver to be included in generic discovery (e.g. scanning).
*/
public async installDriver(driverId: string, hubId: string, channelId: string): Promise<void> {
return this.client.put(`${hubId}/drivers/${driverId}`, { channelId })
}

/**
* Change the driver for a device to the one specified by driverId.
*/
public async switchDriver(driverId: string, hubId: string, deviceId: string): Promise<void> {
return this.client.patch(`${hubId}/childdevice/${deviceId}`, { driverId })
}

/**
* Removes a driver from being installed on a hub. This will allow the hub to clean up the
* deleted driver. However, all dependent devices need to be removed for cleanup to fully occur.
*/
public async uninstallDriver(driverId: string, hubId: string): Promise<void> {
return this.client.delete(`${hubId}/drivers/${driverId}`)
}

/**
* List drivers installed on the hub.
*
* @param deviceId When included, limit the drivers to those marked as matching the specified device.
*/
public async listInstalled(hubId: string, deviceId?: string): Promise<InstalledDriver[]> {
const params = deviceId ? { deviceId } : undefined
return this.client.get(`${hubId}/drivers`, params)
}

public async getInstalled(hubId: string, driverId: string): Promise<InstalledDriver> {
return this.client.get(`${hubId}/drivers/${driverId}`)
}

/**
* Returns the list of driver channels the hub is currently subscribed to.
* Currently only returns the driver channel type.
*/
public async enrolledChannels(hubId: string): Promise<EnrolledChannel[]> {
return this.client.get(`${hubId}/channels`, { channelType: 'DRIVERS' })
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export * from './endpoint/devicepreferences'
export * from './endpoint/deviceprofiles'
export * from './endpoint/devices'
export * from './endpoint/drivers'
export * from './endpoint/history'
export * from './endpoint/hubdevices'
export * from './endpoint/installedapps'
export * from './endpoint/locations'
export * from './endpoint/modes'
Expand Down
3 changes: 3 additions & 0 deletions src/st-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ChannelsEndpoint } from './endpoint/channels'
import { DevicesEndpoint } from './endpoint/devices'
import { DriversEndpoint } from './endpoint/drivers'
import { HistoryEndpoint } from './endpoint/history'
import { HubdevicesEndpoint } from './endpoint/hubdevices'
import { InstalledAppsEndpoint } from './endpoint/installedapps'
import { ModesEndpoint } from './endpoint/modes'
import { LocationsEndpoint } from './endpoint/locations'
Expand All @@ -36,6 +37,7 @@ export class SmartThingsClient extends RESTClient {
public readonly devices: DevicesEndpoint
public readonly drivers: DriversEndpoint
public readonly history: HistoryEndpoint
public readonly hubdevices: HubdevicesEndpoint
public readonly installedApps: InstalledAppsEndpoint
public readonly modes: ModesEndpoint
public readonly notifications: NotificationsEndpoint
Expand All @@ -62,6 +64,7 @@ export class SmartThingsClient extends RESTClient {
this.devices = new DevicesEndpoint(this.config)
this.drivers = new DriversEndpoint(this.config)
this.history = new HistoryEndpoint(this.config)
this.hubdevices = new HubdevicesEndpoint(this.config)
this.installedApps = new InstalledAppsEndpoint(this.config)
this.locations = new LocationsEndpoint(this.config)
this.modes = new ModesEndpoint(this.config)
Expand Down
84 changes: 84 additions & 0 deletions test/unit/hubdevices.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { NoOpAuthenticator } from '../../src/authenticator'
import { HubdevicesEndpoint } from '../../src/endpoint/hubdevices'
import { EndpointClient } from '../../src/endpoint-client'



describe('HubdevicesEndpoint', () => {
afterEach(() => {
jest.clearAllMocks()
})

const getSpy = jest.spyOn(EndpointClient.prototype, 'get').mockImplementation()
const putSpy = jest.spyOn(EndpointClient.prototype, 'put').mockImplementation()
const patchSpy = jest.spyOn(EndpointClient.prototype, 'patch').mockImplementation()
const deleteSpy = jest.spyOn(EndpointClient.prototype, 'delete')

const authenticator = new NoOpAuthenticator()
const hubdevicesEndpoint = new HubdevicesEndpoint({ authenticator })

test('installDriver', async () => {
putSpy.mockImplementationOnce(() => Promise.resolve())

await expect(hubdevicesEndpoint.installDriver('driver-id', 'hub-id', 'channel-id')).resolves.not.toThrow()

expect(putSpy).toHaveBeenCalledTimes(1)
expect(putSpy).toHaveBeenCalledWith('hub-id/drivers/driver-id', { channelId: 'channel-id' })
})

test('switchDriver', async () => {
patchSpy.mockImplementationOnce(() => Promise.resolve())

await expect(hubdevicesEndpoint.switchDriver('driver-id', 'hub-id', 'device-id')).resolves.not.toThrow()

expect(patchSpy).toHaveBeenCalledTimes(1)
expect(patchSpy).toHaveBeenCalledWith('hub-id/childdevice/device-id', { driverId: 'driver-id' })
})

test('uninstallDriver', async () => {
deleteSpy.mockImplementationOnce(() => Promise.resolve())

await expect(hubdevicesEndpoint.uninstallDriver('driver-id', 'hub-id')).resolves.not.toThrow()

expect(deleteSpy).toHaveBeenCalledTimes(1)
expect(deleteSpy).toHaveBeenCalledWith('hub-id/drivers/driver-id')
})

describe('listInstalled', () => {
it('allows for no device', async () => {
getSpy.mockImplementationOnce(() => Promise.resolve())

await expect(hubdevicesEndpoint.listInstalled('hub-id')).resolves.not.toThrow()

expect(getSpy).toHaveBeenCalledTimes(1)
expect(getSpy).toHaveBeenCalledWith('hub-id/drivers', undefined)
})

it('includes device when specified', async () => {
getSpy.mockImplementationOnce(() => Promise.resolve())

await expect(hubdevicesEndpoint.listInstalled('hub-id', 'device-id')).resolves.not.toThrow()

expect(getSpy).toHaveBeenCalledTimes(1)
expect(getSpy).toHaveBeenCalledWith('hub-id/drivers', { deviceId: 'device-id' })
})
})

test('getInstalled', async () => {
getSpy.mockImplementationOnce(() => Promise.resolve())

await expect(hubdevicesEndpoint.getInstalled('hub-id', 'driver-id')).resolves.not.toThrow()

expect(getSpy).toHaveBeenCalledTimes(1)
expect(getSpy).toHaveBeenCalledWith('hub-id/drivers/driver-id')
})

test('enrolledChannels', async () => {
getSpy.mockImplementationOnce(() => Promise.resolve())

await expect(hubdevicesEndpoint.enrolledChannels('hub-id')).resolves.not.toThrow()

expect(getSpy).toHaveBeenCalledTimes(1)
expect(getSpy).toHaveBeenCalledWith('hub-id/channels', { channelType: 'DRIVERS' })
})
})

0 comments on commit 1bcdf62

Please sign in to comment.