Node Server Development ======================= Background ~~~~~~~~~~ Node servers in Polyglot are nothing more than stand alone processes that are managed by Polyglot. Polyglot communicates with the node servers by reading the STDOUT and STDERR streams as well as writing to the STDIN stream. STDIN and STDOUT messages are JSON formatted commands that are documented in :doc:`nsapi`. File Structure ~~~~~~~~~~~~~~ Node servers are defined in self contained folders. The name given to this folder will be the node server ID and must be unique form all other node servers. New node servers can be stored in Polyglot's configuration directory in the folder titled `node_servers`. Inside of this folder, at least the following three files must exist. * *profile.zip* is the profile that must be uploaded to the ISY describing the node server. This file is documented in the ISY Node Server API documentation. * *instructions.txt* should be a file containing instructions on the use of the node server documented using `markdown `_. The contents of this file will be formatted and displayed on the frontend. * *server.json* is the metadata used by Polyglot to identify the node server. This file is documented in the next section. The rest of the node server's folder should contain the code required to execute the node server and all necessary libraries with the exception of those explicitly included as part of the Polyglot distribution. Node servers are executed in special directories in the user's configuration directory. Each node server type is assigned its own directory. Any required inforation may be written to this directory. Keep in mind, that all running node servers of the same type will share the same directory. Server Metadata ~~~~~~~~~~~~~~~ The *server.json* file in the node server source directory is a JSON formatted file that informs Polyglot of how the node server is executed as well as other important details about the node server. The file contains a dictionary formatted object with specific fields. A sample *server.json* is included below. It has been extracted from the Philips Hue node server. .. code-block:: json { "name": "Phillips Hue", "docs": "https://www.universal-devices.com/", "type": "python", "executable": "hue.py", "description": "Connect Phillips Hue Personal Wireless Lighting system to the ISY994.", "notice": "\"Hue Personal Wireless Lighting\" is a trademark owned by Koninklijke Philips Electronics N.V., see www.meethue.com for more information. This Node Server is neither developed by nor endorsed by the Philips organization.", "credits": [ { "title": "phue: A Python library for Philips Hue", "author": "Nathanaël Lécaudé (studioimaginaire)", "version": "0.9", "date": "May 18, 2015", "source": "https://github.com/studioimaginaire/phue/tree/c48845992b476f4b1de9549ea5b5277399f56581", "license": "https://raw.githubusercontent.com/studioimaginaire/phue/c48845992b476f4b1de9549ea5b5277399f56581/LICENSE" } ] } Below is a description of the required fields: * *name* is the name of the node server type as it will be displayed to the user. * *docs* is a link to an appropriate website about the node server. This value is not currently displayed anywhere. * *type* is the node server executable type. This instructs Polyglot as to how the node server should be launched. Currently, only *python* is accepted. * *executable* is the file that Polyglot should execute to start the node server process. * *description* is a short description of the node server that will be displayed to the user on the frontend. * *notice* contains any important notices the user might need to know. * *credits* is a list of dictionaries indicating all third party library used in the node server. Some open source projects require that they be credited some where in the project. Others do not. Either way, it is nice to give credit here. When including a third party library in your node server, ensure that it is licensed for commercial use. In the credits list: * *title* is the title of the third party library. * *author* is the author of the third party library. * *version* is the appropriate versioning tag used to identify the third party library. * *date* is the date the third party library was either released or obtained. * *source* is a link to the library's source code. * *license* is a link to the library's license file. Ensure that this is a static link whose contents cannot be changed. Linking to a specific GitHub commit is handy for this. It can be a good idea to check the formatting of this file with a JSON linter before attempting to load the node server in Polyglot. If this file cannot be read, for whatever reason, the node server will not appear in the Polyglot frontend and an error will be logged. Python Development ~~~~~~~~~~~~~~~~~~ A Python 2.7 compatible implimentation of the API is provided with Polyglot to assist in Node Server development. It may be easily imported as shown below. In the future, more libraries may be made available and more languages may be supported. .. code-block:: python from polyglot import nodeserver_api The provided Node Server Library exposes all of the `ISY controller's Node Server RESTful API `_ as is. Data recieved by Polyglot's web server is parsed and directed immediately to the node server process via this library. The library will also send messages back up to Polyglot to be transmitted directly to the ISY. The only exception to this rule is that node ID's will not have the node server ID prefix prepended to them. It will also be expected that the node server will not prepend these prefixes. Polyglot will handle the node ID prefixes on behalf of the node servers. There also exists, in the Python library, some abstract classes that may be used to ease the development of a new node server. Except in rare cases where it may not be appropriate, it is recomended that these be used. When Python is used to develop node server, the Polyglot environment is loaded into the Python path. This environment includes the `Requests library `_. Python Polyglot Library ~~~~~~~~~~~~~~~~~~~~~~~ Summary ------- .. automodule:: polyglot.nodeserver_api Custom Node Types ----------------- When creating a new node server, each node type that will be controlled by the server must be defined. This abstract class may be used as a skeleton for each node type. When inheriting this class, a new method should be defined for each command that the node can perform. Additionally, the _drivers and _commands attributes should be overwritten to define the drivers and commands relevant to the node. .. autoclass:: polyglot.nodeserver_api.Node :members: :private-members: Polyglot API Implimentation --------------------------- This class impliments the Polyglot API and calls registered functions when the API is invoked. This class is a singleton and will not allow itself to be initiated more than once. This class binds itself to the STDIN stream to accept commands from Polyglot. To create a connection in your node server to Polyglot, use something similar to the following. This creates the connection, connect to Polyglot, and then waits for the Node Server's configuration to be received. The configuration will be the first command received from Polyglot and will never be sent again after the first transmission.:: poly = PolyglotConnector() poly.connect() poly.wait_for_config() Then, commands can be sent upstream to Polyglot or to the ISY by using the connector's methods.:: poly.send_error('This is an error message. It will be in Polyglot\'s log.') poly.add_node('node_id_1', 'NODE_DEFINITION', 'node_id_0', 'New Node') poly.report_status('node_id_1', 'ST', value=55, uom=51) poly.remove_node('node_id_1') To respond to commands received from Polyglot and the ISY, handlers must be registered for events. The handlers arguments will be the parameters specified in the API for that event. This will look something like the following.:: def status_handler(node_address, request_id=None): print('Status Event Handler Called') poly.listen('status', status_handler) Now, when the ISY requests a status update from Polyglot, this function will be called. Handlers will not be called in the node server's main thread. .. autoclass:: polyglot.nodeserver_api.PolyglotConnector :members: Node Server Classes ------------------- .. autoclass:: polyglot.nodeserver_api.NodeServer :members: .. autoclass:: polyglot.nodeserver_api.SimpleNodeServer :members: Helper Functions ---------------- .. autofunction:: polyglot.nodeserver_api.auto_request_report