{"_id":"59d8135746041d0026f51335","project":"568bdc1483d2061900d86cdc","version":{"_id":"59a72290d61777001b6c42c3","project":"568bdc1483d2061900d86cdc","__v":19,"createdAt":"2017-08-30T20:39:44.453Z","releaseDate":"2017-08-30T20:39:44.453Z","categories":["59a7236e3fe4d90025117c10","59a72eb6cb0db3001b84cfe2","59a734eb757d030019b85af8","59c0243b1b2d07001a9d2b76","59c035e42126e10028effb12","59c06c40df5b3c0010584a13","59c1a5852cabe5002641a3e7","59c2fb00b2b45c0010b7a3d7","59c32ceb9aea850010ac4130","59c32e6e190c90003cb0d12f","59c33affb2b45c0010b7aa23","59c7dfa457bd8200105444dc","59c7e975c50cf30010d712a0","59cffdef0cd4dd0010294d54","59d0622ca91a810032c8f60c","59d06733c1aec60026253065","59d174d44ac471001a07b123","59d5a5e323e6e800103defb2","59ecf1d8ed507c001c52b255"],"is_deprecated":false,"is_hidden":false,"is_beta":true,"is_stable":true,"codename":"","version_clean":"0.0.0","version":"0"},"category":{"_id":"59d0622ca91a810032c8f60c","project":"568bdc1483d2061900d86cdc","version":"59a72290d61777001b6c42c3","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2017-10-01T03:34:04.474Z","from_sync":false,"order":4,"slug":"edge-server-plugins","title":"Edge Server"},"user":"58cc41f21751ce2f003be3b7","__v":0,"parentDoc":null,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2017-10-06T23:35:51.708Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":2,"body":"# Overview\n\nPlugins are the mechanism through which the Edge server communicates with local devices.\n\nPlugins are responsible for:\n1. Discovering local devices\n2. Coordinating communication between devices and the Edge server\n3. Tracking the state changes for each device.\n4. Relaying this information to the corresponding Droplit service.\n\nA good plugin is one that is focused on a common application-level protocol. For example, WeMo devices all work over HTTP with a common SOAP structure behind them, so a single generic plugin can work. Attempting to make a general HTTP device plugin would be poor design, however, because the structure of data used to communicate with devices over HTTP varies widely. Plugins should be based around a principle of common structure, encapsulating common functionality.\n\nPlugins may be written in both TypeScript and native JavaScript. The example plugins `droplit-plugin-ts-example` and `droplit-plugin-js-example` (for each language accordingly) are included with the Edge server. These exammples may be useful as starting points to create further custom plugins, if desired.\n\nNode.js can use native libraries through a C++ addon module using node-gyp.\n\n# Plugin Pieces\n\n## Service Classes\nA plugin author should be familiar with how service classes are built, and how to use their components.\n\n## localId\nA `localId` uniquely identifies a device within a plugin. The Edge server is responsible for mapping between the device ID from the Droplit service and the `localId`. While a `localId` only needs to be unique within a plugin, globally unique identifiers that a device may have make for a good localId (e.g., MAC address or a UUID from SSDP).\n\n## address\nAn `address` is the identifier used to address the device on the network. The form the address takes depends on the network protocol the device uses. E.g., a WiFi device would use an IP address or URI for its address.\n\n# Discovery\n\nEach plugin is responsible for discovering the devices that it is compatible with.\n\nThe Edge server starts discovery by calling the `discover` method on the plugin. This is first called after all plugins have been initialized. Later, it is called on a routine interval, to discover any devices added after the first call. Discovery calls to each plugin are staggered to avoid interference between plugins. Beyond the `discover` call, the discovery process is implementation agnostic.\n\nEvery time a new device is discovered, the plugin should call the `onDeviceInfo` method. This will register the device and its details with the Droplit service based on the provided `localId`. Subsequent calls will update the device details with the specified properties. Only make additional calls when something has actually changed.\n\nDiscovered devices should be cached upon discovery. This helps track which discoveries are new, and makes it easier to track property changes.\n\nThe following example illustrates discovering a new device, caching it, and registering the device to the Droplit service.\n\n```\n| JavaScript                                         | TypeScript                                         |\n|----------------------------------------------------|----------------------------------------------------|\n| // setup device discoverer                         | // setup device discoverer                         |\n| const discoverer = new Discoverer();               | const discoverer = new Discoverer();               |\n| // Cache of known devices                          | // Cache of known devices                          |\n| const devices = new Map();                         | const devices: Map<string, Device> =               |\n|                                                    |   new Map<string, Device>();                       |\n|                                                    |                                                    |\n| // Handle device being discovered                  | // Handle device being discovered                  |\n| discoverer.on('device discovered', device => {     | discoverer.on(                                     |\n|                                                    |   'device discovered', (device: Device) => {       |\n|                                                    |                                                    |\n|   // Skip the device if discovered already         |   // Skip the device if discovered already         |\n|   if (devices.has(device.identifier))              |   if (devices.has(device.identifier))              |\n|     return;                                        |     return;                                        |\n|                                                    |                                                    |\n|   // Cache the device to record discovery          |   // Cache the device to record discovery          |\n|   devices.set(device.identifier, device);          |   devices.set(device.identifier, device);          |\n|                                                    |                                                    |\n|   // Send device info                              |   // Send device info                              |\n|   onDeviceInfo({                                   |   onDeviceInfo({                                   |\n|     localId: device.identifier,                    |     localId: device.identifier,                    |\n|     address: device.ipAddress,                     |     address: device.ipAddress,                     |\n|     deviceMeta: {                                  |     deviceMeta: {                                  |\n|       customName: device.name,                     |       customName: device.name,                     |\n|       manufacturer: 'Sirius Cybernetics Corp.',    |       manufacturer: 'Sirius Cybernetics Corp.',    |\n|       modelName: 'Nutrimatics Drinks Despenser'    |       modelName: 'Nutrimatics Drinks Despenser'    |\n|     },                                             |     },                                             |\n|     services: [ 'BinarySwitch' ]                   |     services: [ 'BinarySwitch' ]                   |\n|   });                                              |   });                                              |\n| });                                                | });                                                |\n|                                                    |                                                    |\n| // The Edge process calls the discover function    | // The Edge process calls the discover function    |\n| function discover() {                              | function discover() {                              |\n|   discoverer.discover(); // do discovery           |   discoverer.discover(); // do discovery           |\n| }                                                  | }                                                  |\n```\n\nEvery plugin must handle the case of an address change (e.g., the IP address for a WiFi address). Changes in the device addresses are often found in subsequent discovery calls by comparing the known unique identifier with the address. When this happens, the Droplit service should be notified with an `onDeviceInfo` call containing only the `localId` and `address`.\n\nThe following example illustrates updating the device address after it has been changed:\n\n```\n| JavaScript                                         | TypeScript                                         |\n|----------------------------------------------------|----------------------------------------------------|\n| discoverer.on('ipChange', data => {                | discoverer.on('ipChange', (data: Device) => {      |\n|   const device =                                   |   const device =                                   |\n|     this.devices.get(data.identifier);             |     this.devices.get(data.identifier);             |\n|   // If the device isn't defined, return           |   // If the device isn't defined, return           |\n|   if (!device)                                     |   if (!device)                                     |\n|     return;                                        |     return;                                        |\n|   // Update the address in the cache               |   // Update the address in the cache               |\n|   device.address = data.address;                   |   device.address = data.address;                   |\n|                                                    |                                                    |\n|   // Only send the localId and changed data        |   // Only send the localId and changed data        |\n|   onDeviceInfo({                                   |   onDeviceInfo({                                   |\n|     localId: device.identifier,                    |     localId: device.identifier,                    |\n|     address: device.address                        |     address: device.address                        |\n|   });                                              |   });                                              |\n| });                                                | });                                                |\n```\n\nThe Edge server will call the plugin’s `dropDevice` method when a device is deleted (e.g., through the Droplit service’s REST API). On `dropDevice`, the plugin should clear the specified device from its local cache. Dropped devices may be rediscovered on subsequent discovery calls.\n\nThe following example illustrates a `dropDevice` implementation.\n\n```\n| JavaScript                                         | TypeScript                                         |\n|----------------------------------------------------|----------------------------------------------------|\n| function dropDevice(localId) {                     | function dropDevice(localId: string): boolean {    |\n|    // The device is not previously known           |     // The device is not previously known          |\n|    const device = devices.get(localId);            |     const device = devices.get(localId);           |\n|    if (!device)                                    |     if (!device)                                   |\n|      return false;                                 |       return false;                                |\n|                                                    |                                                    |\n|    // Remove the device from the cache             |     // Remove the device from the cache            |\n|    this.devices.delete(device.identifier);         |     this.devices.delete(device.identifier);        |\n|    return true;                                    |     return true;                                   |\n| }                                                  | }                                                  |\n```\n\n# Edge as a Device\n\nThe Droplit Edge software connects remote devices to the Droplit.io cloud for monitoring and control. Devices connected through the edge software are referred to as _downstream devices_.\n\nDownstream devices are independently addressable and controlled without needing a specialized knowledge of how they are connected up to the cloud. The node running the Droplit Edge software is also represented as a device in its respective environment.\n\nEdge devices can expose services that can be monitored or controlled as well. Services local to the edge device are referred to as _local services_.\n\n## Local Services\n\nLocal services are exposed by configuring a plugin to handle requests for a particular service class. All requests made to the edge device accessing that service class will be routed to that plugin. This does not include requests addressed to downstream devices.\n\nEx., if you wish to handle the hypothetical `DoStuff` service with the hypothetical `plugin-stuff` plugin, it would be configured as:\n\n```\n{\n  \"plugins\": {\n    \"plugin-stuff\": {\n      \"enabled\": true,\n      \"localServices\": [ \"DoStuff\" ]\n    }\n  }\n}\n```\n\nFrom within the plugin the `localId` used for services is `.`.\n\nThis localId will come in on any property accessor or method call and should be set for any upstream message such as an event or property changed message.\n\nEx., sending a `notifyStuff` event on `DoStuff`.\n\n```\nthis.onEvents([{ localId: '.', service: 'DoStuff', member: 'notifyStuff' }])\n```\n\n# Data Structures\nThere are common data structures used by various plugin interfaces.\n\n## DeviceInfo\nThe `DeviceInfo` structure is an `object` representing all of the data about a device, except for service property states. This structure is used by `onDeviceInfo`.\n\n### localId\nThe `localId` of the device represented by a `string`.\n\n### address\nThe `address` of the device represented by a `string`.\n\n### services\nThe `services` supported by the device represented by an `array` of `strings`.\n\n### deviceMeta\nThe `deviceMeta` property is an `object` representing device metadata. This device metadata is directly mapped to general [device metadata](doc:metadata). The explicit properties on `deviceMeta` get mapped to system metadata keys while custom properties get mapped to regular metadata keys.\n\nSystem keys:\n* `customName`: A custom name stored on device. Many devices support the ability for a user to set a name through their first-party device — for such devices, this name should be used for custom name.\n* `manufacturer`: The device manufacturer.\n* `modelDescription`: A brief description of the device.\n* `modelName`: The name of the device’s model.\n* `modelNumber`: The device model number.\n\n## DeviceServiceMember\nThe `DeviceServiceMember` structure is an `object` used by property setters/getters, method calls, and events.\n\n### localId\nThe `localId` of the device represented by a `string`.\n\n### address\nThe `address` of the device represented by a `string`.\n\n### service\nThe name of the service class represented by a `string`\n\n### index\nThe index on the service class. Omit for service class implementations that are not indexed. Represented by a `string`.\n\n### member\nThe name of the member on the service class represented by a `string`.\n\n### value\nA value to pass with the Edge event. For properties, this is the value of the property. For method calls, this is that method arguments. For events, this is an event value. this value may be `any` valid JS type.\n\n# Plugin interface\nA plugin is expected to expose an object implementing a particular set of interfaces in order to work with the Edge server. For the convenience, Droplit provides a [`droplit-plugin` module](https://www.npmjs.com/package/droplit-plugin) (supporting both TypeScript and native JavaScript) to make implementing a plugin as easy as extending a ES6 class; however, using the module is not strictly necessary for implementing a plugin (if for example you do not wish to use ES6+).\n\nThe following are plugin methods (signatures defined in TypeScript notation):\n## discover\n`discover(): void` — discover all devices\n`discover` is called by the Edge server to tell the plugin to attempt to find new devices (e.g., invoking SSDP).\n\n## drop device\n`dropDevice(localId: string): boolean` — clear device information from memory\n`dropDevice` is called by the Edge server to tell the plugin to remove knowledge of a specified device from any sort of cache. The device is specified with the `localId` parameter. The return value is a boolean signifying whether the device was successfully removed or not (e.g., the specified id is not one that the plugin is aware of).\n\n## onDeviceInfo\n`onDeviceInfo(deviceInfo: DeviceInfo, callback?: (deviceInfo: DeviceInfo) => {}): void`\n`onDeviceInfo` is called by the Edge server to update the non-service data of a device. It is expected that `onDeviceInfo` is called on device discovery in order to first initialize device data; subsequent calls may be made to modify this data.\n\nThe callback will be called with the complete deviceInfo data as it is known by the server in response to the update. This is useful if there is existing known data cached in the `droplit.io` service that is not easily discoverable from the plugin; on discovery, the plugin may send its incomplete information and fill-in the remainder with what the server already knows.\n\n## onEvents\n`onEvents(events: DeviceServiceMember[]): void`\n`onEvents` is called within the plugin to communicate to the Edge server when an event occurs. The `events` parameter is in the form of an array to allow for reporting multiple events being raised simultaneously.\n\nex. report `MotionSensor.motion` event occurring for device `1`\n\n```\nthis.onEvents({\n  localId: '1',\n  service: 'MotionSensor',\n  member: 'motion'\n});\n```\n\n## onPropertiesChanged\n`onPropertiesChanged(properties: DeviceServiceMember[]): void`\n`onPropertiesChanged` is called within the plugin to communicate to the Edge server when a property’s value has changed. This method should not be called unless the underlying state value is actually different than the previously known value. Initial property values after device discovery should be reported with this method. The `properties` parameter is in the form of an array to allow for reporting multiple property changes simultaneously.\n\nex. report `BinarySwitch.switch` being set to `on` for device `1`\n\n```\nthis.onPropertyChanged({\n  localId: '1',\n  service: 'BinarySwitch',\n  member: 'switch',\n  value: 'on'\n});\n```\n\n## services\n`services: any` — maps service class members to functions\n`services` is a property of the plugin object that maps [service class](doc:services) members to functions with an object of key-value pairs with members as keys and functions as values.\n\nex. maps the members of the BinarySwitch service class\n\n```\nthis.services = {\n    BinarySwitch: {\n        get_switch: this.getSwitch,\n        set_switch: this.setSwitch,\n        switchOff: this.switchOff,\n        switchOn: this.switchOn\n    }\n};\n```\n\nFor members that are methods, the mapping key is the member name. For members that are properties, a mapping is expected for the property getter and setter (read-only properties may omit a setter). The format of these mapping keys is to prefix the member name with `get_` and `set_` for getters and setters accordingly.\n\nThe mapped functions have difference signatures depending on the type of member.\nproperty getter: `function(localId: string, callback: (value: any) => void, index: string): boolean`\nproperty setter: `function(localId: string, value: any, index: string): boolean`\nmethod: `function(localId: string, value: any, callback: (value: any) => void, index: string): boolean`\n\n`localId` — the id of the device to invoke the getter/setter/method call on\n`index` — for indexed devices, this specifies the index to use on the device specified by the localId\n`value` — the value to set a property to or call a method with\n`callback` — invoke to return the get value for a getter or a value to return in response to a method call when it is a `request` rather than a `call`","excerpt":"","slug":"edge-creating-plugins","type":"basic","title":"Creating Plugins"}
# Overview Plugins are the mechanism through which the Edge server communicates with local devices. Plugins are responsible for: 1. Discovering local devices 2. Coordinating communication between devices and the Edge server 3. Tracking the state changes for each device. 4. Relaying this information to the corresponding Droplit service. A good plugin is one that is focused on a common application-level protocol. For example, WeMo devices all work over HTTP with a common SOAP structure behind them, so a single generic plugin can work. Attempting to make a general HTTP device plugin would be poor design, however, because the structure of data used to communicate with devices over HTTP varies widely. Plugins should be based around a principle of common structure, encapsulating common functionality. Plugins may be written in both TypeScript and native JavaScript. The example plugins `droplit-plugin-ts-example` and `droplit-plugin-js-example` (for each language accordingly) are included with the Edge server. These exammples may be useful as starting points to create further custom plugins, if desired. Node.js can use native libraries through a C++ addon module using node-gyp. # Plugin Pieces ## Service Classes A plugin author should be familiar with how service classes are built, and how to use their components. ## localId A `localId` uniquely identifies a device within a plugin. The Edge server is responsible for mapping between the device ID from the Droplit service and the `localId`. While a `localId` only needs to be unique within a plugin, globally unique identifiers that a device may have make for a good localId (e.g., MAC address or a UUID from SSDP). ## address An `address` is the identifier used to address the device on the network. The form the address takes depends on the network protocol the device uses. E.g., a WiFi device would use an IP address or URI for its address. # Discovery Each plugin is responsible for discovering the devices that it is compatible with. The Edge server starts discovery by calling the `discover` method on the plugin. This is first called after all plugins have been initialized. Later, it is called on a routine interval, to discover any devices added after the first call. Discovery calls to each plugin are staggered to avoid interference between plugins. Beyond the `discover` call, the discovery process is implementation agnostic. Every time a new device is discovered, the plugin should call the `onDeviceInfo` method. This will register the device and its details with the Droplit service based on the provided `localId`. Subsequent calls will update the device details with the specified properties. Only make additional calls when something has actually changed. Discovered devices should be cached upon discovery. This helps track which discoveries are new, and makes it easier to track property changes. The following example illustrates discovering a new device, caching it, and registering the device to the Droplit service. ``` | JavaScript | TypeScript | |----------------------------------------------------|----------------------------------------------------| | // setup device discoverer | // setup device discoverer | | const discoverer = new Discoverer(); | const discoverer = new Discoverer(); | | // Cache of known devices | // Cache of known devices | | const devices = new Map(); | const devices: Map<string, Device> = | | | new Map<string, Device>(); | | | | | // Handle device being discovered | // Handle device being discovered | | discoverer.on('device discovered', device => { | discoverer.on( | | | 'device discovered', (device: Device) => { | | | | | // Skip the device if discovered already | // Skip the device if discovered already | | if (devices.has(device.identifier)) | if (devices.has(device.identifier)) | | return; | return; | | | | | // Cache the device to record discovery | // Cache the device to record discovery | | devices.set(device.identifier, device); | devices.set(device.identifier, device); | | | | | // Send device info | // Send device info | | onDeviceInfo({ | onDeviceInfo({ | | localId: device.identifier, | localId: device.identifier, | | address: device.ipAddress, | address: device.ipAddress, | | deviceMeta: { | deviceMeta: { | | customName: device.name, | customName: device.name, | | manufacturer: 'Sirius Cybernetics Corp.', | manufacturer: 'Sirius Cybernetics Corp.', | | modelName: 'Nutrimatics Drinks Despenser' | modelName: 'Nutrimatics Drinks Despenser' | | }, | }, | | services: [ 'BinarySwitch' ] | services: [ 'BinarySwitch' ] | | }); | }); | | }); | }); | | | | | // The Edge process calls the discover function | // The Edge process calls the discover function | | function discover() { | function discover() { | | discoverer.discover(); // do discovery | discoverer.discover(); // do discovery | | } | } | ``` Every plugin must handle the case of an address change (e.g., the IP address for a WiFi address). Changes in the device addresses are often found in subsequent discovery calls by comparing the known unique identifier with the address. When this happens, the Droplit service should be notified with an `onDeviceInfo` call containing only the `localId` and `address`. The following example illustrates updating the device address after it has been changed: ``` | JavaScript | TypeScript | |----------------------------------------------------|----------------------------------------------------| | discoverer.on('ipChange', data => { | discoverer.on('ipChange', (data: Device) => { | | const device = | const device = | | this.devices.get(data.identifier); | this.devices.get(data.identifier); | | // If the device isn't defined, return | // If the device isn't defined, return | | if (!device) | if (!device) | | return; | return; | | // Update the address in the cache | // Update the address in the cache | | device.address = data.address; | device.address = data.address; | | | | | // Only send the localId and changed data | // Only send the localId and changed data | | onDeviceInfo({ | onDeviceInfo({ | | localId: device.identifier, | localId: device.identifier, | | address: device.address | address: device.address | | }); | }); | | }); | }); | ``` The Edge server will call the plugin’s `dropDevice` method when a device is deleted (e.g., through the Droplit service’s REST API). On `dropDevice`, the plugin should clear the specified device from its local cache. Dropped devices may be rediscovered on subsequent discovery calls. The following example illustrates a `dropDevice` implementation. ``` | JavaScript | TypeScript | |----------------------------------------------------|----------------------------------------------------| | function dropDevice(localId) { | function dropDevice(localId: string): boolean { | | // The device is not previously known | // The device is not previously known | | const device = devices.get(localId); | const device = devices.get(localId); | | if (!device) | if (!device) | | return false; | return false; | | | | | // Remove the device from the cache | // Remove the device from the cache | | this.devices.delete(device.identifier); | this.devices.delete(device.identifier); | | return true; | return true; | | } | } | ``` # Edge as a Device The Droplit Edge software connects remote devices to the Droplit.io cloud for monitoring and control. Devices connected through the edge software are referred to as _downstream devices_. Downstream devices are independently addressable and controlled without needing a specialized knowledge of how they are connected up to the cloud. The node running the Droplit Edge software is also represented as a device in its respective environment. Edge devices can expose services that can be monitored or controlled as well. Services local to the edge device are referred to as _local services_. ## Local Services Local services are exposed by configuring a plugin to handle requests for a particular service class. All requests made to the edge device accessing that service class will be routed to that plugin. This does not include requests addressed to downstream devices. Ex., if you wish to handle the hypothetical `DoStuff` service with the hypothetical `plugin-stuff` plugin, it would be configured as: ``` { "plugins": { "plugin-stuff": { "enabled": true, "localServices": [ "DoStuff" ] } } } ``` From within the plugin the `localId` used for services is `.`. This localId will come in on any property accessor or method call and should be set for any upstream message such as an event or property changed message. Ex., sending a `notifyStuff` event on `DoStuff`. ``` this.onEvents([{ localId: '.', service: 'DoStuff', member: 'notifyStuff' }]) ``` # Data Structures There are common data structures used by various plugin interfaces. ## DeviceInfo The `DeviceInfo` structure is an `object` representing all of the data about a device, except for service property states. This structure is used by `onDeviceInfo`. ### localId The `localId` of the device represented by a `string`. ### address The `address` of the device represented by a `string`. ### services The `services` supported by the device represented by an `array` of `strings`. ### deviceMeta The `deviceMeta` property is an `object` representing device metadata. This device metadata is directly mapped to general [device metadata](doc:metadata). The explicit properties on `deviceMeta` get mapped to system metadata keys while custom properties get mapped to regular metadata keys. System keys: * `customName`: A custom name stored on device. Many devices support the ability for a user to set a name through their first-party device — for such devices, this name should be used for custom name. * `manufacturer`: The device manufacturer. * `modelDescription`: A brief description of the device. * `modelName`: The name of the device’s model. * `modelNumber`: The device model number. ## DeviceServiceMember The `DeviceServiceMember` structure is an `object` used by property setters/getters, method calls, and events. ### localId The `localId` of the device represented by a `string`. ### address The `address` of the device represented by a `string`. ### service The name of the service class represented by a `string` ### index The index on the service class. Omit for service class implementations that are not indexed. Represented by a `string`. ### member The name of the member on the service class represented by a `string`. ### value A value to pass with the Edge event. For properties, this is the value of the property. For method calls, this is that method arguments. For events, this is an event value. this value may be `any` valid JS type. # Plugin interface A plugin is expected to expose an object implementing a particular set of interfaces in order to work with the Edge server. For the convenience, Droplit provides a [`droplit-plugin` module](https://www.npmjs.com/package/droplit-plugin) (supporting both TypeScript and native JavaScript) to make implementing a plugin as easy as extending a ES6 class; however, using the module is not strictly necessary for implementing a plugin (if for example you do not wish to use ES6+). The following are plugin methods (signatures defined in TypeScript notation): ## discover `discover(): void` — discover all devices `discover` is called by the Edge server to tell the plugin to attempt to find new devices (e.g., invoking SSDP). ## drop device `dropDevice(localId: string): boolean` — clear device information from memory `dropDevice` is called by the Edge server to tell the plugin to remove knowledge of a specified device from any sort of cache. The device is specified with the `localId` parameter. The return value is a boolean signifying whether the device was successfully removed or not (e.g., the specified id is not one that the plugin is aware of). ## onDeviceInfo `onDeviceInfo(deviceInfo: DeviceInfo, callback?: (deviceInfo: DeviceInfo) => {}): void` `onDeviceInfo` is called by the Edge server to update the non-service data of a device. It is expected that `onDeviceInfo` is called on device discovery in order to first initialize device data; subsequent calls may be made to modify this data. The callback will be called with the complete deviceInfo data as it is known by the server in response to the update. This is useful if there is existing known data cached in the `droplit.io` service that is not easily discoverable from the plugin; on discovery, the plugin may send its incomplete information and fill-in the remainder with what the server already knows. ## onEvents `onEvents(events: DeviceServiceMember[]): void` `onEvents` is called within the plugin to communicate to the Edge server when an event occurs. The `events` parameter is in the form of an array to allow for reporting multiple events being raised simultaneously. ex. report `MotionSensor.motion` event occurring for device `1` ``` this.onEvents({ localId: '1', service: 'MotionSensor', member: 'motion' }); ``` ## onPropertiesChanged `onPropertiesChanged(properties: DeviceServiceMember[]): void` `onPropertiesChanged` is called within the plugin to communicate to the Edge server when a property’s value has changed. This method should not be called unless the underlying state value is actually different than the previously known value. Initial property values after device discovery should be reported with this method. The `properties` parameter is in the form of an array to allow for reporting multiple property changes simultaneously. ex. report `BinarySwitch.switch` being set to `on` for device `1` ``` this.onPropertyChanged({ localId: '1', service: 'BinarySwitch', member: 'switch', value: 'on' }); ``` ## services `services: any` — maps service class members to functions `services` is a property of the plugin object that maps [service class](doc:services) members to functions with an object of key-value pairs with members as keys and functions as values. ex. maps the members of the BinarySwitch service class ``` this.services = { BinarySwitch: { get_switch: this.getSwitch, set_switch: this.setSwitch, switchOff: this.switchOff, switchOn: this.switchOn } }; ``` For members that are methods, the mapping key is the member name. For members that are properties, a mapping is expected for the property getter and setter (read-only properties may omit a setter). The format of these mapping keys is to prefix the member name with `get_` and `set_` for getters and setters accordingly. The mapped functions have difference signatures depending on the type of member. property getter: `function(localId: string, callback: (value: any) => void, index: string): boolean` property setter: `function(localId: string, value: any, index: string): boolean` method: `function(localId: string, value: any, callback: (value: any) => void, index: string): boolean` `localId` — the id of the device to invoke the getter/setter/method call on `index` — for indexed devices, this specifies the index to use on the device specified by the localId `value` — the value to set a property to or call a method with `callback` — invoke to return the get value for a getter or a value to return in response to a method call when it is a `request` rather than a `call`