VOLTTRON Documentation Release 3.5RC1

The VOLTTRON Developer Team

June 30, 2016

Contents

1 Installation 3 1.1 Source Code...... 3 1.2 Bootstrap...... 3 1.3 Activate...... 3 1.4 Testing...... 4 1.5 Execution...... 4 1.6 Next Steps...... 4

2 License 5

3 Community Resources 7 3.1 Community Resources...... 7

4 Overview 9 4.1 Definition of Terms...... 9 4.2 Platform Background...... 9 4.3 Platform Components...... 10 4.4 Agents in the Platform...... 10 4.5 VOLTTRON Version History...... 11

5 VOLTTRON Core Services 13 5.1 Base Platform Functionality...... 13 5.2 VOLTTRON Historian Framework...... 19 5.3 VOLTTRON Driver Framework...... 25 5.4 VOLTTRON Message Bus...... 43 5.5 Service Agents...... 61 5.6 VOLTTRON Security...... 85 5.7 VOLTTRON PNNL Licensed Code...... 86

6 Developer Resources 91 6.1 Getting Started...... 91 6.2 Agent Development...... 103 6.3 Deployment Advice...... 156 6.4 Walkthroughs...... 161 6.5 Eclipse IDE Setup...... 173

7 Example Agents and Uitlities 191 7.1 Example Agents...... 191 7.2 Utilities...... 199

i 8 Scalability Experiments 205 8.1 Scalability Setup...... 205 8.2 Improvements Based on Results...... 207 8.3 Scalability Planning...... 207

9 Development History and Roadmap 211 9.1 Migration from 1.2 - 2.0...... 211 9.2 Migration from 2.x to 3.x...... 211 9.3 3.X drivers...... 213 9.4 Migration from 3.0 to 3.5...... 214 9.5 Change Logs...... 215 9.6 Transitioning from 1.x to 2.x...... 217 9.7 VOLTTRON Versions...... 218

10 API Documentation 221 10.1 Services...... 221 10.2 Applications...... 260 10.3 Examples...... 285 10.4 Volttron...... 294

11 Support 349

12 Indices and tables 351

Python Module Index 353

ii VOLTTRON Documentation, Release 3.5RC1

Contents:

Contents 1 VOLTTRON Documentation, Release 3.5RC1

2 Contents CHAPTER 1

Installation

VOLTTRON requires the following dependencies in order to bootstrap the development environment. • Essential build tools (gcc, make, autodev-tools, etc.) • Python development files (headers) • Openssl. • Git (Optional) On Debian-based systems, these can all be installed with the following command: sudo apt-get update sudo apt-get install build-essential python-dev openssl libssl-dev libevent-dev git

On Arch , the following command will install the dependencies: sudo pacman -S base-devel python2 openssl libssl-dev libsodium

1.1 Source Code

To work with the latest devlopment code clone from the develop branch by using the following git command. git clone https://github.com/VOLTTRON/volttron/ -b develop

To work with the latest stable code clone the master branch using the following git command. git clone https://github.com/VOLTTRON/volttron/

1.2 Bootstrap

To create a development environment, execute the following in the project root directory. python2.7 bootstrap.py

1.3 Activate

Activating the shell sets the correct environment for executing a volttron instance. From the project root directory execute the following.

3 VOLTTRON Documentation, Release 3.5RC1

source env/bin/activate

Note that an ‘activated’ command prompt is like the following .. code-block:: bash (volttron)user@machine $

1.4 Testing

VOLTTRON uses py.test as a framework for executing tests. py.test is not installed with the distribution by default. To install py.test and it’s dependencies execute the following: python bootstrap.py --testing

To run all of the tests in the volttron repository execute the following in the root directory using an activated command prompt: py.test

1.5 Execution

To start a default instance of volttron from an activated command prompt execute the following. volttron -vv

Or to start volttron in the background with logging to a file called volttron.log execute the following. volttron -vv -l volttron.log&

1.6 Next Steps

• agent-development

4 Chapter 1. Installation CHAPTER 2

License

Copyright (c) 2013-2016, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830

5 VOLTTRON Documentation, Release 3.5RC1

6 Chapter 2. License CHAPTER 3

Community Resources

3.1 Community Resources

3.1.1 Mailing List

Join the mailing list by emailing [email protected].

3.1.2 Office Hours

PNNL hosts office hours every other week on Fridays at 10:30 AM (PST). These meetings are designed to be very informal where VOLTTRON developers can answer specific questions about the inner workings of VOLTTRON. These meetings are also available for topical discussions of different aspects of the VOLTTRON platform. Currently the office hours are available through a Lync meeting. Join Lync Meeting

Dial in Number: 1-866-528-1882 Conference Number: 886916

7 VOLTTRON Documentation, Release 3.5RC1

8 Chapter 3. Community Resources CHAPTER 4

Overview

4.1 Definition of Terms

• JsonRPC: JSON-encoded remote procedure call • JSON: JavaScript object notation is a text-based, human-readable, open data interchange format, similar to XML, but much better • Publish/subscribe: A message delivery pattern where senders (publishers) and receivers (subscribers) do not communicate directly nor necessarily have knowledge of each other, but instead exchange messages through an intermediary based on a mutual class or topic • ZeroMQ or ØMQ: A library used for interprocess and intercomputer communicationc • Modbus: Communications protocol for talking with industrial electronic devices • SSH: Secure shell is a network protocol providing encryption and authentication of data using public-key cryp- tography • SSL: Secure sockets layer is a technology for encryption and authentication of network traffic based on a chain of trust • TLS: Transport layer security is the successor to SSL

4.2 Platform Background

VOLTTRON serves as an integrating platform for the components of the

Transactional Network project. It provides an environment for agent execution and serves as a single point of contact for interfacing with devices (RTU HVACs, power meters, etc.), external resources, and platform services such as data archival and retrieval. The VOLTTRON platform provides a collection of utility and helper classes which simplifies agent development. | In the Transactional Network project, VOLTTRON connects devices (RTU HVACs, power meters, etc.) to applications implemented in the platform and in the cloud, a data historian, and signals from the power grid. It also provides helper classes to ease development and deployment of agents into the environment.

9 VOLTTRON Documentation, Release 3.5RC1

4.3 Platform Components

The components of the Transactional Network are illustrated in the

figure above. | The Device Interface communicates to the HVAC Controller using Modbus. It periodically scrapes data off the controller and both pushes to the sMAP historian and publishes it to the Message Bus on a topic for each device. The Device Interface also responds to lock and control commands published on the requests topic. Agents must first request and receive a lock on a device for a certain time period. During this time, they have exclusive control of the device and may issues commands. | The sMAP agent in the figure represents the Archiver Agent which allows agents to request data from sMAP over the Message Bus. The Archiver Agent isolates agents from the details of the Historian and would allow the platform to use a different or multiple historian solutions (sMAP, a database, and a some other site).

4.4 Agents in the Platform

Agents deployed on VOLTTRON can perform one or more roles which can be broadly classified into the following groups: • Platform Agents: Agents which are part of the platform and provide a service to other agents. Examples are agents which interface with devices to publish readings and handle control signals from other agents. • Cloud Agents: These agents represent a remote application which needs access to the messages and data on the platform. This agent would subscribe to topics of interest to the remote application and would also allow it publish data to the platform. • Control Agents: These agents control the devices of interest and interact with other resources to achieve some goal. Platform Services:

10 Chapter 4. Overview VOLTTRON Documentation, Release 3.5RC1

• Message Bus: All agents and services publish and subscribe to topics on the message bus. This provides a single interface that abstracts the details of devices and agents from each other. Components in the platform basically produce and consume events. • Weather Information: This agent periodically retrieves data from the Weather Underground site. It then refor- mats it and publishes it out to the platform on a weather topic. • Modbus-based device interface: The Modbus driver publishes device data onto the message bus. It also handles the locking of devices to prevent multiple conflicting directives. • Application Scheduling: This service allows the scheduling of agents’ access to devices in order to prevent conflicts. • Logging service: Agents can publish arbitrary strings to a logging topic and this service will push them to the sMAP historian for later analysis.

4.5 VOLTTRON Version History

VOLTTRON 1.0 – 1.2 • Agent execution platform • Message bus • Modbus and BACnet drivers • Historian • Data logger • Device scheduling • Device actuation • Multi-node communication • Weather service VOLTTRON 2.0 • Advanced Security Features • Guaranteed resource allocation to agents using execution contracts • Signing and verification of agent packaging • Agent mobility • Admin can send agents to another platform • Agent can request to move • Enhanced command framework VOLTTRON 3.0 • Modularize Data Historian • Modularize Device Drivers • Secure and accountable communication using the VIP • Web Console for Monitoring and Administering VOLTTRON Deployments

4.5. VOLTTRON Version History 11 VOLTTRON Documentation, Release 3.5RC1

12 Chapter 4. Overview CHAPTER 5

VOLTTRON Core Services

5.1 Base Platform Functionality

5.1.1 Used Environmental Variables

VOLTTRON_VIP_ADDR - The router address an agent is/will attempt to connect to. | AGENT_CONFIG - The path to a configuration file to use during agent launch | VOLTTRON_HOME - The home directory where the volttron instances is located.

5.1.2 Agent Autostart

An agent can be setup to start when the platform is started with the “enable” command. This command also allows a priority to be set (0-100, default 50) so that agents can be started after any dependencies. This command can also be used with the –tag or –name options. volttron-ctl enable

5.1.3 Agent Lifecyle Management

The VOLTTRON platform has several commands for controlling the lifecycle of agents. This page discusses how to use them, for details of operation please see PlatformConfiguration These examples assume the VOLTTRON environment has been activated (. env/bin/activate). If not, add “bin/” to all commands.

Agent Packaging

The “volttron-pkg” command is used for packaging and configuring agents. It is not necessary to have the platform running to use this command. The platform uses Python Wheel for its packaging and follows the Wheel naming convention. To create an agent package, call volttron-pkg . For instance: volttron-pkg package examples/ListenerAgent The package command uses the setup.py in the agent directory to create the package. The name and version number portion of the Wheel filename come from this. The resulting wheels are created at “~/.volttron/packaged”.

13 VOLTTRON Documentation, Release 3.5RC1

For example: ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl.

Agent Configuration

Agent packages are configured with the volttron-pkg configure command. It is suggested that this file use json formatting but the agent can be written to interpret any format it requires. The configuration of a particular agent is opaque to the VOLTTRON platform. The location of the agent config file is passed as an environmental variable “AGENT_CONFIG” which the provided utilities read in and pass to the agent. An example config file passing in some parameters: {

"agentid":"listener1", "message":"hello" }

Agent Installation and Removal

Agents are installed into the platform using: volttron-ctl install . | When agents are installed onto a platform, it creates a uuid for that instance of an agent. This allows multiple instances of the same agent package to be installed on the platform. Agents can also be installed with a tag by using: volttron-ctl install = This allows the user to refer to the agent with “–tag ” instead of the uuid when issuing commands. This tag can also distinguish instances of an agent from each other. A stopped agent can be removed with: • volttron-ctl remove • volttron-ctl remove --tag • volttron-ctl remove --name Removal by tag and name potentially allows multiple agents to be removed at once and should be used with caution. A “-f” option is required to delete more than one agent at a time.

Agent Control

Starting and Stopping an Agent

Agent that are installed in the platform can be launched with the “start” command. By default this operates off the agent’s UUID but can be used with “–tag” or “–name” to launch agents by those attributes. This can allow multiple agents to be started at once. For instance: volttron-ctl start --name myagent-0.1 would start all instances of that agent regardless of their uuid, tag, or configuration information. After an agent is started, it will show up in AgentStatus as “running” with a process id. Similarly, volttron-ctl stop can also operate off the tag and name of agent(s). After an agent is stopped, it will show an exit code of 0 in AgentStatus

14 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Running an agent

For testing purposes, an agent package not installed in the platform can be run by using: volttron-ctl run .

Agent Status volttron-ctl list lists the agents installed on the platform and their priority | The volttron-ctl status shows the list of installed agents and whether they are running or have exited. | See AgentStatus for more details.

5.1.4 Agent List Display

AGENT TAG PRI d listeneragent-3.0 listener 30 2 testeragent-0.1 volttron-ctl list shows the agents which have been installed on the platform along with their uuid, associated tag, and priority. • uuid is the first column of the display and is displayed as the shorted unique portion. Using this portion, agents can be started, stopped, removed, etc. • AGENT is the “name” of this agent based on the name of the wheel file which was installed. Agents can be controlled with this using “–name ”. Note, if multiple instances of a wheel are installed they will all have the same name and can be controlled as a group. • TAG is a user provided tag which makes it simpler to track and refer to agents. Using “–tag ” agents can be controlled using this • PRI is the priority for agents which have been “enabled” using the volttron-ctl enable command. When enabled, agents will be automatically started in priority order along with the platform.

Agent Status Display

AGENT TAG STATUS d listeneragent-3.0 listener running [3813] 2 testeragent-0.1 0 volttron-ctl statu shows a list of all agents installed on the platform and their current status. • uuid is the first column of the display and is displayed as the shorted unique portion. Using this portion, agents can be started, stopped, removed, etc. • AGENT is the “name” of this agent based on the name of the wheel file which was installed. Agents can be controlled with this using “–name ”. Note, if multiple instances of a wheel are installed they will all have the same name and can be controlled as a group. • TAG is a user provided tag which makes it simpler to track and refer to agents. Using “–tag ” agents can be controlled using this

5.1. Base Platform Functionality 15 VOLTTRON Documentation, Release 3.5RC1

• STATUS is the current condition of the agent. If the agent is currently executing, it has “running” and the process id of the agent. If the agent is not running, the exit code is shown.

5.1.5 Tagging Agents

Agents can be tagged as they are installed with: volttron-ctl install = Agents can be tagged after installation with: volttron-ctl tag Agents can be “tagged” to provide a meaningful user defined way to reference the agent instead of the uuid or the name. This allows users to differentiate between instances of agents which use the same codebase but are configured differently. For instance, the AFDDAgent can be configured to work against a single HVAC unit and can have any number of instances running on one platform. A tagging scheme for this could be by unit: afdd-rtu1, afdd-rtu2, etc. Commands which operate off an agent’s UUID can optionally operate off the tag by using “–tag ”. This can use wildcards to catch multiple agents at once.

5.1.6 Platform Commands

With the exception of packaged agent wheel files, all VOLTTRON files for a platform instance are stored under a single directory known as the VOLTTRON home. This home directory is set via the VOLTTRON_HOME environment variable and defaults to ~/.volttron. Multiple instances of the platform may exist under the same account on a system by setting the VOLTTRON_HOME environment variable appropriately before executing VOLTTRON commands. Configuration files use a modified INI format where section names are command names for which the settings in the section apply. Settings before the first section are considered global and will be used by all commands for which the settings are valid. Settings keys are long options (with or without the opening –) and are followed by a colon (:) or equal (=) and then the value. Boolean options need not include the separator or value, but may specify a value of 1, yes, or true for true or 0, no, or false for false. A default configuration file, $VOLTTRON_HOME/config, may be created to override default options. If it exists, it will be automatically parsed before all other command-line options. To skip parsing the default configuration file, either move the file out of the way or set the SKIP_VOLTTRON_CONFIG environment variable. All commands and sub-commands have help available with “-h” or “–help”. Additional configuration files may be specified with “-c” or “–config”. To specify a log file, use “-l” or “–log”. bin/volttron -c config.ini -l volttron.log

Full options: optional arguments: -c FILE, --config FILE read configuration from FILE -l FILE, --log FILE send log output to FILE instead of stderr -L FILE, --log-config FILE read logging configuration from FILE -q, --quiet decrease logger verboseness; may be used multiple times -v, --verbose increase logger verboseness; may be used multiple times --verboseness LEVEL set logger verboseness --help show this help message and exit --version show program's version number and exit

16 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1 volttron-ctl commands: install install agent from wheel tag set, show, or remove agent tag remove remove agent list list installed agent status show status of agents clear clear status of defunct agents enable enable agent to start automatically disable prevent agent from start automatically start start installed agent stop stop agent restart restart agent run start any agent by path shutdown stop all agents with Volttron Restricted package installed and enabled send send mobile agent to and start on a remote platform

volttron-pkg commands: usage: volttron-pkg [-h] {package,repackage,configure} ... optional arguments: -h, --help show this help message and exit subcommands: valid subcommands

{package,repackage,configure} additional help package Create agent package (whl) from a directory or installed agent name. repackage Creates agent package from a currently installed agent. configure add a configuration file to an agent package volttron-pkg commands (with Volttron Restricted package installed and enabled): usage: volttron-pkg [-h] [-l FILE] [-L FILE] [-q] [-v] [--verboseness LEVEL] {package,repackage,configure,create_ca,create_cert,sign,verify} ...

VOLTTRON packaging and signing utility optional arguments: -h, --help show this help message and exit -l FILE, --log FILE send log output to FILE instead of stderr -L FILE, --log-config FILE read logging configuration from FILE -q, --quiet decrease logger verboseness; may be used multiple times -v, --verbose increase logger verboseness; may be used multiple times --verboseness LEVEL set logger verboseness subcommands: valid subcommands

5.1. Base Platform Functionality 17 VOLTTRON Documentation, Release 3.5RC1

{package,repackage,configure,create_ca,create_cert,sign,verify} additional help package Create agent package (whl) from a directory or installed agent name. repackage Creates agent package from a currently installed agent. configure add a configuration file to an agent package sign sign a package verify verify an agent package

5.1.7 VOLTTRON Config File

The VOLTTRON platform config file can contain any of the command line arguments for starting the platform... -c FILE, --config FILE read configuration from FILE -l FILE, --log FILE send log output to FILE instead of stderr -L FILE, --log-config FILE read logging configuration from FILE -q, --quiet decrease logger verboseness; may be used multiple times -v, --verbose increase logger verboseness; may be used multiple times --verboseness LEVEL set logger verboseness --help show this help message and exit --version show program's version number and exit agent options: --autostart automatically start enabled agents and services --publish-address ZMQADDR ZeroMQ URL for used for agent publishing --subscribe-address ZMQADDR ZeroMQ URL for used for agent subscriptions control options: --control-socket FILE path to socket used for control messages --allow-root allow root to connect to control socket --allow-users LIST users allowed to connect to control socket --allow-groups LIST user groups allowed to connect to control socket

Boolean options, which take no argument, may be inversed by prefixing the | option with no- (e.g. –autostart may be inversed using –no-autostart).

5.1.8 VOLTTRON Environment

By default, the VOLTTRON projects bases its files out of VOLTTRON_HOME which defaults to “~/.volttron”. • $VOLTTRON_HOME/agents contains the agents installed on the platform • $VOLTTRON_HOME/certificates contains the certificates for use with the Licensed VOLTTRON code.

18 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

• $VOLTTRON_HOME/run contains files create by the platform during execution. The main ones are the 0MQ files created for publish and subcribe. • $VOLTTRON_HOME/ssh keys used by agent mobility in the Licensed VOLTTRON code • $VOLTTRON_HOME/config Default location to place a config file to override any platform settings. • $VOLTTRON_HOME/packaged is where agent packages created with ‘volttron-pkg package are created

5.2 VOLTTRON Historian Framework

5.2.1 Analytics Historian

An Analytics Historian (OpenEIS) has been developed to integrate real time data ingestion into the OpenEIS platform. In order for the OpenEIS historian to be able to communicate with an OpenEIS server a datasource must be created on the OpenEIS server. The process of creating a dataset is documented in the OpenEIS User’s Guide under Creating a Dataset heading. Once a dataset is created you will be able to add datasets through the configuration file. An example configuration for the historian is as follows: { # The agent id is used for display in volttron central. "agentid":"openeishistorian", # The vip identity to use with this historian. # should not be a platform.historian! # # Default value is un referenced because it listens specifically to the bus. #"identity": "openeis.historian",

# Require connection section for all historians. The openeis historian # requires a url for the openis server and login credentials for publishing # to the correct user's dataset. "connection":{ "type":"openeis", "params":{ # The server that is running openeis # the rest path for the dataset is dataset/append/{id} # and will be populated from the topic_dataset list below. "uri":"http://localhost:8000",

# Openeis requires a username/password combination in order to # login to the site via rest or the ui. # "login":"volttron", "password":"volttron" } },

# All datasets that are going to be recorded by this historian need to be # defined here. # # A dataset definition consists of the following parts # "ds1": { # # The dataset id that was created in openeis. # "dataset_id": 1, # # Setting to 1 allows only the caching of data that actually meets

5.2. VOLTTRON Historian Framework 19 VOLTTRON Documentation, Release 3.5RC1

# the mapped point criteria for this dataset. # Defaults to 0 # "ignore_unmapped_points": 0, # # An ordered list of points that are to be posted to openeis. The # points must contain a key specifying the incoming topic with the # value an openeis schema point: #[ # {"rtu4/OutsideAirTemp": "campus1/building1/rtu4/OutdoorAirTemperature"} #] # }, "dataset_definitions":{ "ds1":{ "dataset_id":1, "ignore_unmapped_points":0, "points":[ {"campus1/building1/OutsideAirTemp":"campus1/building1/OutdoorAirTemperature"}, {"campus1/building1/HVACStatus":"campus1/building1/HVACStatus"}, {"campus1/building1/CompressorStatus":"campus1/building1/LightingStatus"} ] } #, #"ds2": { # "id": 2, # "points": [ # "rtu4/OutsideAirTemp", # "rtu4/MixedAirTemp" #] #} } }

5.2.2 Forward Historian

The forward historian can be found in the core services directory. Use it to send information to another Volttron instance.

Configuration

The default configuration file is services/core/ForwardHistorian/config . Change destination-vip to point towards the foreign Volttron instance. { "agentid": "forwarder" "destination-vip": "ipc://@/home/volttron/.volttron/run/vip.socket" }

Adding the configuration option below will limit the backup cache to n gigabytes. This will keep your hard drive from filling up if the agent is disconnected from its target for a long time. "backup_storage_limit_gb": n

See Also

Historians Historians

20 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

5.2.3 Historian Topic Syntax

Each historian will subscribe to the following message bus topics (actuator/, anaylsis/, record/* and devices/*). For each of these topics there is a different message syntax that must be adhered to in order for the correct interpretation of the data being specified. record/* The record topic is the most flexible of all of the topics. This topic allows any serializable message to be published to any topic under the root topic ‘/record’. ————————————————————————— —————————————————————————————- Note: this topic is not recommended to plot as the structure of the messages are not necessarily numeric. # Example messages that can be published

# Dictionary data {'foo':'wolrd'}

# Numerical data 52

# Time data (note not a datatime object) '2015-12-02T11:06:32.252626' devices/*

The devices topic is meant to be data structured from a scraping of a ModBus or BacNet device. Cur- rently there are drivers for both of these protocols write data to the message bus in the proper format. VOLTTRON drivers also publish an aggregation of points in an “all” topic. The all topic is what is read and published to a historian. Both the all topic have the same header information, but the mes- sage body for each is slightly different. For a complete working example of these messages please see https://github.com/VOLTTRON/volttron/blob/develop/examples/ExampleSubscriber/subscriber/subscriber_agent.py # Header contains the data associated with the message. { # python code to get this is # from datetime import datetime # from volttron.platform.messaging import headers as header_mod #{ # headers_mod.DATE: datetime.utcnow().isoformat() + 'Z' #} "Date": "2015-11-17T21:24:10.189393Z" }

# Individual Point topic # Messages contains a two element list. The first element contains an individual reading. The second # element contains a list of meta data. { [52.5, {'units': 'F', 'tz': 'UTC', 'type': 'float'}] }

# ALL TOPIC # Messages contains a two element list. The first element contains a dictionary of all points # under a #specific parent. While the second element contains a dictionary of meta data for each of the specified points. For example devices/pnnl/building/OutsideAirTemperature and # devices/pnnl/building/MixedAirTemperature ALL message would be created like: { [ {"OutsideAirTemperature ": 52.5, "MixedAirTemperature ": 58.5},

5.2. VOLTTRON Historian Framework 21 VOLTTRON Documentation, Release 3.5RC1

{ "OutsideAirTemperature ": {'units': 'F', 'tz': 'UTC', 'type': 'float'}, "MixedAirTemperature ": {'units': 'F', 'tz': 'UTC', 'type': 'float'} } ], ...... ]

5.2.4 Mongo Historian

Prerequisites

1. Mongodb

Setup mongodb based on using one of the three below scripts. 1. Install as root on Redhat or Cent OS sudo scripts/historian-scripts/root_install_mongo_rhel.sh

The above script will prompt user for os version, db user name, password and database name Once installed you can start and stop the service using the command: sudo service mongod [start|stop|service] 2. Install as root on Ubuntu sudo scripts/historian-scripts/root_install_mongo_ubuntu.sh

The above script will prompt user for os version, db user name, password and database name Once installed you can start and stop the service using the command: sudo service mongod [start|stop|service] 3. Install as non root user on any Linux machine scripts/historian-scripts/install_mongodb.sh

Usage: install_mongodb.sh [-h] [-d download_url] [-i install_dir] [-c config_file] [-s] Optional arguments: -s setup admin user and test collection after install and startup -d download url. defaults to https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.2.4.tgz -i install_dir. defaults to current_dir/mongo_install -c config file to be used for mongodb startup. Defaults to default_mongodb.conf in the same directory as this script.Any datapath mentioned in the config file should already exist and should have write access to the current user -h print this help message

2. Mongodb connector

This historian requires a mongodb connector installed in your activated volttron environment to talk to mongodb. Please execute the following from an activated shell in order to install it.

22 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

pip install pymongo

5.2.5 Platform Historian

A platform historian is a “friendly named” historian on a VOLTTRON instance. It always has the identity (see vip) of platform.historian. A platform historian is made available to a volttron central agent for monitoring of the VOLT- TRON instances health and plotting topics from the platform historian. In order for one of the (historians)[Historians] to be turned into a platform historian the identity keyword must be added to it’s configuration with the value of plat- form.historian. The following configuration file shows a sqlite based platform historian configuration. { "agentid":"sqlhistorian-sqlite", "identity":"platform.historian", "connection":{ "type":"sqlite", "params":{ "database":"~/.volttron/data/platform.historian.sqlite" } } }

The platform historian will publish data about the current environment to the following topics. These topics can be pasted into the volttron central environment and will be able to be graphed appropriately. Index Topic 1 datalogger/log/platform/status/cpu/times_percent/guest_nice 2 datalogger/log/platform/status/cpu/times_percent/system 3 datalogger/log/platform/status/cpu/percent 4 datalogger/log/platform/status/cpu/times_percent/irq 5 datalogger/log/platform/status/cpu/times_percent/steal 6 datalogger/log/platform/status/cpu/times_percent/user 7 datalogger/log/platform/status/cpu/times_percent/nice 8 datalogger/log/platform/status/cpu/times_percent/iowait 9 datalogger/log/platform/status/cpu/times_percent/idle 10 datalogger/log/platform/status/cpu/times_percent/guest 11 datalogger/log/platform/status/cpu/times_percent/softirq

5.2.6 SQL Historian

An SQL Historian is available as a core service. The sql historian has been programmed to allow for inconsistent network connectivity (automatic re-connection to tcp based databases). All additions to the historian are batched and wrapped within a transaction with commit and rollback functions properly implemented. This allows the maximum throughput of data with the most protection. The following example configurations show the different options available for configuring the SQL Historian Agent.

MySQL Specifics

MySQL requires a third party driver (mysql-connector) to be installed in order for it to work. Please execute the following from an activated shell in order to install it. pip install --allow-external mysql-connector-python mysql-connector-python

5.2. VOLTTRON Historian Framework 23 VOLTTRON Documentation, Release 3.5RC1

In addition, the mysql database must be created and permissions granted for select, insert and update before the agent is started. In order to support timestamp with microsec- onds you need at least MySql 5.6.4. Please see this MySql documentation for more details | The following is a minimal configuration file for using a MySQL based historian. Other options are available and are documented http://dev.mysql.com/doc/connector-python/en/connector-python-connectargs.html. Not all parameters have been tested, use at your own risk. { "agentid":"sqlhistorian-mysql", "connection":{ "type":"mysql", "params":{ "host":"localhost", "port": 3306, "database":"volttron", "user":"user", "passwd":"pass" } } }

Sqlite3 Specifics

An Sqlite Historian provides a convenient solution for under powered systems. The database is parameter is a location on the file system. By default it is relative to the agents installation directory, however it will respect a rooted or relative path to the database. { "agentid":"sqlhistorian-sqlite", "connection":{ "type":"sqlite", "params":{ "database":"data/historian.sqlite", } } }

5.2.7 VOLTTRON Historian Framework Introduction

Historian Agents are the way by which device, actuator, datalogger, and analysis are captured and stored in some sort of data store. Historian Agents for sMAP, general support for SQL based database (sqlite and MySql), and an OpenEIS stores already exist. <<<<<<< Updated upstream - sMAP Historian - SQL Historian - OpenEIS Historian ======- sMAP Histo- rian - SQL Historian - OpenEIS Historian >>>>>>> Stashed changes Other implementations of historians can be created by following the developing historian agents section of the wiki. Historians are all built upon the BaseHistorian which provides general functionality the specific implementations build upon.

By default the base historian will listen to 4 separate root topics

24 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

(datalogger/, record/, actuators/, and device/. Each of root topics has a specific message syntax that it is expecting | for incoming data. Messages that are published to actuator are assumed to be part of the actuation process. Messages published to datalogger will be assumed to be timepoint data that is composed of units and specific types with the assumption that they have the ability to be graphed easily. Messages published to devices are data that comes directly from drivers. Finally Messages that are published to record will be handled as string data and can be customized to the user specific situation. Please consult the Historian Topic Syntax page for a specific syntax.

This base historian will cache all received messages to a local

database | before publishing it to the historian. This allows recovery for unexpected | happenings before the successful writing of data to the historian.

5.2.8 sMAP Historian

This historian allows VOLTTRON data to be published to an sMAP server. This replaces the DataLogger functionality of V2.0 as well as the capability of 2.0 drivers to publish directly to sMAP. To configure this historian the following must be in the config file. This file is setup to point at the available Test Instance of sMAP. For reliable storage, please setup your own instance. { "agentid":"smap_historian", "source":"MyTestSource", "archiver_url":"http://smap-test.cloudapp.net", "key":"LEq1cEGc04RtcKX6riiX7eaML8Z82xEgQrp7" }

That’s it! With this configuration, data will be pulled off the message bus and published to your sMAP server.

5.3 VOLTTRON Driver Framework

All Voltton drivers are implemented through the Master Driver Agent and are technically sub-agents running in the same process as the Master Driver Agent. Each of these driver sub-agents is responsible for creating an interface to a single device. Creating that interface is facilitated by an instance of an interface class. Currently there are two interface classes included: Modbus and BACnet.

5.3.1 Automatically Generating BACnet Configuration Files

Included with the platform are two scripts for finding and configuring BACnet devices. These scripts are located in scripts/bacnet. bacnet_scan.py will scan the network for devices. grab_bacnet_config.py creates a CSV file for the BACnet driver that can be used as a starting point for creating your own register configuration. Both scripts are configured with the file BACpypes.ini.

Configuring the Utilities

While running both scripts create a temporary virtual BACnet device using the bacpypes library. The virtual device must be configured properly in order to work. This configuration is stored in scripts/bacnet/BACpypes.ini and will be read automatically when the utility is run.

5.3. VOLTTRON Driver Framework 25 VOLTTRON Documentation, Release 3.5RC1

The only value that (usually) needs to be changed is the address field. **This address bound to the port on the machine you are running the script from, NOT A TARGET DEVICE! ** This value should be set to the IP address of the network interface used to communicate with the remote device. If there is more than one network interface the address of the interface connected to a network that can reach the device must be used. In Linux you can usually get the addresses bound to all interfaces by running ifconfig from the command line. If a different outgoing port other than the default 47808 must be used the it can be specified as part of the address in the form

: In some cases the netmask of the network will be needed for proper configuration. This can be done following this format
/: where is the netmask length. The most commmon value is 24. See http://www.computerhope.com/jargon/n/netmask.htm In some cases you may also need to specify a different device ID by changing the value of objectIdentifier so the virtual BACnet device does not conflict with any devices on the network. objectIdentifier defaults to 599.

Sample BACpypes.ini

[BACpypes] objectName: Betelgeuse address: 10.0.2.15/24 objectIdentifier: 599 maxApduLengthAccepted: 1024 segmentationSupported: segmentedBoth vendorIdentifier: 15

Scanning for BACnet Devices

If the addresses for BACnet devices are unknown they can be discovered using the bacnet_scan.py utility. To run the utility simply execute the following command: python bacnet_scan.py and expect output similar to this: Device Address =

Device Id = 699 maxAPDULengthAccepted = 1024 segmentationSupported = segmentedBoth vendorID = 15

Device Address = Device Id = 540011 maxAPDULengthAccepted = 480 segmentationSupported = segmentedBoth vendorID = 5

26 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Reading Output

The address where the device can be reached is listed on the Device Address line. The BACnet device ID is listed on the Device Id line. The remaining lines are informational and not needed to configure the BACnet driver. For the first example the IP address 192.168.1.42 can be used to reach the device. The second device is behind a BACnet router and can be reached at 1002:11. See RouterAddressing Remote Station addressing.

Options

• --address ADDRESS Send the who is request only to a specific address. Useful as a way to ping devices on a network that blocks broadcast traffic. • --range LOW HIGH Specify the device ID range for the results. Useful for filtering. • --timeout SECONDS Specify how long to wait for responses to the original broadcast. This defaults to 5 which should be sufficient for most networks.

Automatically Generating a BACnet Registry Configuration File

A CSV registry configuration file for the BACnet driver can be generated with the grab_bacnet_config.py script. This configuration will need to be edited before it can be used. The utility is invoked with the command: python grab_bacnet_config.py This will query the device with the matching device ID for configuration information and print the resulting CSV file to the console.

Note: Previous to VOLTTRON 3.5 grab_bacnet_config.py took the device address as an argument instead of the device ID.

In order to save the configuration to a file use the --file option to sepcify the output file name. Optionally the --address option can be used to specify the address of the target. In some cases this is needed to help establish a route to the device.

Output and Assumptions

Attempts at determining if a point is writable proved too unreliable. Therefore all points are considered to be read only in the output. The only property for which a point is setup for an object is presentValue. By default the Volttron Point Name is set to the value of the name property of the BACnet object on the device. In most cases this name is vague. No attempt is made at divining a better name. A duplicate of “Volttron Point Name” solumn called “Reference Point Name” is created to so that once “Volttron Point Name” is changed a reference remains to the actual BACnet device object name. Meta data from the objects on the device is used to attempt to put useful info in the Units Unit Details, and Notes columns. Information such as the range of valid values, defaults, the resolution or sensor input, and enumeration or state names are scraped from the device. With a few exceptions “Units” is pulled from the object’s “units” property and given the name used by the bacpypes library to describe it. If a value in the Units column takes the form

5.3. VOLTTRON Driver Framework 27 VOLTTRON Documentation, Release 3.5RC1

UNKNOWN UNIT ENUM VALUE: then the device is using a nonstandard value for the units on that object.

Problems and Debugging

Typically the utility should run quickly and finish in a few seconds or less. In our testing we have never seen a successful scrape take more than 1 second. If the utility has not finished after about 3 seconds it is probably having trouble communicating with the device and should be stopped. Rerunning with debug output can help diagnose the problem. To output debug messages to the console add the --debug switch to the end of the command line arguments. python grab_bacnet_config.py --file test.csv --debug On a successful run you will see output similar to this: DEBUG:main:initialization DEBUG:main: - args: Namespace(address='10.0.2.20', buggers=False, debug=[], ini=, max_range_report=1e+20, out_file=) DEBUG:main.SynchronousApplication:init (, '10.0.2.15') DEBUG:main:starting build DEBUG:main:pduSource =

DEBUG:main:iAmDeviceIdentifier = ('device', 500) DEBUG:main:maxAPDULengthAccepted = 1024 DEBUG:main:segmentationSupported = segmentedBoth DEBUG:main:vendorID = 5 DEBUG:main:device_name = MS-NCE2560-0 DEBUG:main:description = DEBUG:main:objectCount = 32 DEBUG:main:object name = 2400Stevens/FCB.Local Application.Room Real Temp 2 DEBUG:main: object type = analogInput DEBUG:main: object index = 3000274 DEBUG:main: object units = degreesFahrenheit DEBUG:main: object units details = -50.00 to 250.00 DEBUG:main: object notes = Resolution: 0.1 DEBUG:main:object name = 2400Stevens/FCB.Local Application.Room Real Temp 1 DEBUG:main: object type = analogInput DEBUG:main: object index = 3000275 DEBUG:main: object units = degreesFahrenheit DEBUG:main: object units details = -50.00 to 250.00 DEBUG:main: object notes = Resolution: 0.1 DEBUG:main:object name = 2400Stevens/FCB.Local Application.OSA DEBUG:main: object type = analogInput DEBUG:main: object index = 3000276 DEBUG:main: object units = degreesFahrenheit DEBUG:main: object units details = -50.00 to 250.00 DEBUG:main: object notes = Resolution: 0.1 ... and will finish something like this: ... DEBUG:main:object name = 2400Stevens/FCB.Local Application.MOTOR1-C DEBUG:main: object type = binaryOutput DEBUG:main: object index = 3000263 DEBUG:main: object units = Enum DEBUG:main: object units details = 0-1 (default 0) DEBUG:main: object notes = BinaryPV: 0=inactive, 1=active DEBUG:main:finally

28 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Typically if the BACnet device is unreachable for any reason (wrong IP, network down/unreachable, wrong interface specified, device failure, etc) the scraper will stall at this message: DEBUG:main:starting build

If you have not specified a valid interface in BACpypes.ini you will see the following error with a stack trace: ERROR:main:an error has occurred: [Errno 99] Cannot assign requested address

5.3.2 BACnet Proxy Agent

Introduction

Communication with BACnet device on a network happens via a single virtual BACnet device. Previous versions of Volttron used one virtual device per device on the network. This only worked in a limited number of circumstances. (This problem is fixed in the legacy sMap drivers in Volttron 3.0 only) In the new driver architecture we have a separate agent specifically for communicating with BACnet devices and managing the virtual BACnet device.

Configuration

The agent configuration sets up the virtual BACnet device. { "vip_identity": "platform.bacnet_proxy", "device_address": "10.0.2.15", "max_apdu_length": 1024, "object_id": 599, "object_name": "Volttron BACnet driver", "vendor_id": 15, "segmentation_supported": "segmentedBoth" }

• vip_identity - The VIP identity of the agent. Defaults to platform.bacnet_proxy. This should only be changed if multiple Proxies need to be run for communication with multiple BACnet networks. See Communicating With Multiple BACnet Networks.

BACnet device settings

• device_address - Address bound to the network port over which BACnet communication will happen on the computer running VOLTTRON. This is NOT the address of any target device. See Device Addressing. • object_id - ID of the Device object of the virtual bacnet device. Defaults to 599. Only needs to be changed if there is a conflicting BACnet device ID on your network. These settings determine the capabilities of the virtual BACnet device. BACnet communication happens at the lowest common denominator between two devices. For instance if the BACnet proxy supports segmentation and the target device does not communication will happen without segmentation support and will be subject to those limitations. Consequently there is little reason to change the default settings outside of the max_apdu_length (the default is not the largest possible value). • max_apdu_length - (From bacpypes documentation) BACnet works on lots of different types of networks, from high speed Ethernet to “slower” and “cheaper” ARCNET or MS/TP (a serial bus protocol used for a field bus defined by BACnet). For devices to exchange messages they have to know the maximum size message the device can handle. (End BACpypes docs)

5.3. VOLTTRON Driver Framework 29 VOLTTRON Documentation, Release 3.5RC1

This setting determines the largest APDU accepted by the BACnet virtual device. Valid options are 50, 128, 206, 480, 1024, and 1476. Defaults to 1024.(Optional) • object_name - Name of the object. Defaults to “Volttron BACnet driver”. (Optional) • vendor_id - Vendor ID of the virtual bacnet device. Defaults to 15. (Optional) • segmentation_supported - (From bacpypes documentation) A vast majority of BACnet communications traffic fits in one message, but there can be times when larger messages are convinient and more efficient. Segmentation allows larger messages to be broken up into segemnts and spliced back together. It is not unusual for “low power” field equipment to not support segmentation. (End BACpypes docs) Possible setting are “segmentedBoth” (default), “segmentedTransmit”, “segmentedReceive”, or “noSegmenta- tion” (Optional)

Device Addressing

In some cases it will be needed to specify the subnet mask of the virtual device or a different port number to listen on. The full format of the BACnet device address is

/: where is the port to use and is the netmask length. The most commmon value is 24. See http://www.computerhope.com/jargon/n/netmask.htm For instance if you need to specify a subnet mask of 255.255.255.0 and the IP address bound to the network port is 192.168.1.2 you would use the address 192.168.1.2/24

If your BACnet network is on a different port (47809) besides the default (47808) you would use the address 192.168.1.2:47809

If you need to do both 192.168.1.2/24:47809

Communicating With Multiple BACnet Networks

If two BACnet devices are connected to different ports they are considered to be on different BACnet networks. In order to communicate with both devices you will need to run one BACnet Proxy Agent per network. Each proxy will need to be bound to different ports appropriate to each BACnet network and will need a different VIP identity specified. When configuring drivers you will need to specify which proxy to use by specifying the VIP identity. For example a proxy connected to the default BACnet network { "vip_identity": "platform.bacnet_proxy_1", "device_address": "192.168.1.2/24" }

and another on port 47809 { "vip_identity": "platform.bacnet_proxy_2", "device_address": "192.168.1.2/24:47809" }

30 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

a device one the first network { "driver_config":{"device_address": "1002:12", "proxy_address": "platform.bacnet_proxy_1"}, "campus": "campus", "building": "building", "unit": "bacnet1", "driver_type": "bacnet", "registry_config":"/home/kyle/configs/bacnet.csv", "interval": 60, "timezone": "UTC", "heart_beat_point": "Heartbeat" }

and a device on the second network { "driver_config":{"device_address": "12000:5", "proxy_address": "platform.bacnet_proxy_2"}, "campus": "campus", "building": "building", "unit": "bacnet2", "driver_type": "bacnet", "registry_config":"/home/kyle/configs/bacnet.csv", "interval": 60, "timezone": "UTC", "heart_beat_point": "Heartbeat" }

Notice that both configs use the same registry configuration (/home/kyle/configs/bacnet.csv). This is perfectly fine as long as the registry configuration is appropriate for both devices.

5.3.3 BACnet Router Addressing

The underlying library that Volttron uses for BACnet supports IP to MS/TP routers. Devices behind the router use a Remote Station address in the form :

where is the configured network ID of the router and

is the address of the device behind the router. For example to access the device at
12 for a router configured for 1002 can be accessed with this address: 1002:12

must be number from 0 to 65534 and

must be a number from 0 to 255. This type of address can be used anywhere an address is required in configuration of the Volttron BACnet driver.

Caveats

VOLTTRON uses a UDP broadcast mechanism to establish the route to the device. If the route cannot be established it will fall back to a UDP broadcast for all communication with the device. If the IP network where the router is connected blocks UDP broadcast traffic then these addresses will not work. <<<<<<< Updated upstream

5.3. VOLTTRON Driver Framework 31 VOLTTRON Documentation, Release 3.5RC1

5.3.4 Driver Configuration

The Master Driver Agent manages all device communication. To communicate with devices you must setup and deploy the Master Driver Agent. Configuration for each device consists of 3 parts: • Master Driver Agent configuration file - lists all driver configuration files to load • Driver configuration file - contains the general driver configuration and device settings • Device Register configuration file - contains the settings for each individual data point on the device For each device you must create a driver configuration file, device register configuration file, and an entry in the Master Driver Agent configuration file. Once configured the Master Driver Agent is configured and deployed in a manner similar to any other agent (TODO: insert link to Agent deployment howto). The Master Driver Agent along with Historian Agents replace the functionality of sMap from VOLTTRON 2.0 and thus sMap is no longer a requirement for VOLTTRON.

Master Driver Agent Configuration

The Master Driver Agent configuration consists of a list of device configuration files to load at startup. The user may optionally stagger the start of drivers to improve scalability of the platform by using the staggered_start setting. The following example loads three driver configuration files: { "driver_config_list":[ "/home/volttron-user/configs/test_bacnet1.config", "/home/volttron-user/configs/test_bacnet2.config", "/home/volttron-user/configs/test_modbus1.config" ], "staggered_start": 30.0 }

• driver_config_list - A list of driver configuration files to load at startup. • staggered_start - Spread the scraping and publishing of device data over approximately N seconds. Useful for when the platform scrapes too many devices at once resulting in failed scrapes. An example master driver configuration file can be found here or in the VOLTTRON repository in examples/configurations/drivers/master-driver.agent.

Driver Configuration File

Note: The terms register and point are used interchangeably in the documentation and in the configuration setting names. They have the same meaning.

Each device configuration has the following form: { "driver_config":{"device_address": "10.1.1.5", "device_id": 500},

"driver_type": "bacnet",

32 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

"registry_config":"/home/volttron-user/configs/vav.csv", "interval": 60, "campus": "pnnl", "building": "isb1", "unit": "vav1", "heart_beat_point": "heatbeat" }

The following settings are required for all device configurations: • driver_config - Driver specific setting go here. See below for driver specific settings. • driver_type - Type of driver to use for this device. Currently VOLTTRON includes “bacnet” and “modbus” drivers and a testing driver called “fake”. • registry_config - Registry configuration file for registers on the device. See the Registry Configuration File section below. These settings are optional: • interval - Period which to scrape the device and publish the results in seconds. Defaults to 60 seconds. • heart_beat_point - A Point which to toggle to indicate a heartbeat to the device. A point with this Volttron Point Name must exist in the registry. If this setting is missing the driver will not send a heart beat signal to the device. Heart beats are triggered by the Actuator Agent which must be running to use this feature. These settings are used to create the topic that this device will be referenced by following the VOLTTRON convention of {campus}/{building}/{unit}. This will also be the topic published on when then device is periodically scraped for it’s current state. While all of the settings are optional at least one is required: • campus - Campus portion of the device topic. (Optional) • building - Building portion of the device topic. (Optional) • unit - Unit portion of the device topic. (Optional) • path - Additional topic bits after unit. Useful for specifying sub devices. (Optional) For instance with the above example the topic used to reference this device when making an RPC call would be pnnl/isb1/vav1

Device State Publishes

By default the value of each register on a device is published 4 different ways when the device state is published. Consider the following settings in a Driver Configuration File: { "driver_config":{"device_address": "10.1.1.5", "device_id": 500},

"driver_type": "bacnet", "registry_config":"/home/volttron-user/configs/vav.csv", "campus": "pnnl", "building": "isb1", "unit": "vav1", }

In the vav.csv file is a register with the name temperature. For these examples the current value of the register on the device happens to be 75.2 and the meta data is

5.3. VOLTTRON Driver Framework 33 VOLTTRON Documentation, Release 3.5RC1

{"units":"F"}

When the driver publishes the device state the following 2 things will be published for this register: A “depth first” publish to the topic devices/pnnl/isb1/vav1/temperature with the following message: [75.2,{"units":"F"}]

A “breadth first” publish to the topic devices/temperature/vav1/isb1/pnnl with the follow- ing message: [75.2,{"units":"F"}]

Also these two publishes happen once for all registers: A “depth first” publish to the topic devices/pnnl/isb1/vav1/all with the following message: [{"temperature": 75.2,...}, {"temperature":{"units":"F"},...}]

A “breadth first” publish to the topic devices/all/vav1/isb1/pnnl with the following message: [{"temperature": 75.2,...}, {"temperature":{"units":"F"},...}]

Scalability Settings

In order to improve the scalability of the platform unneeded device state publishes for a device can be turned off. All of the following setting are optional and default to True: • publish_depth_first_all - Enable device state publishes to the topic devices/////all • publish_breadth_first_all - Enable device state publishes to the topic devices/all//// • publish_depth_first - Enable device state publishes to the topic devices///// for each register on the device. • publish_breadth_first - Enable device state publishes to the topic devices/all//// for each register on the device. It is common practice to set publish_breadth_first_all, publish_depth_first, and publish_breadth_first to False unless they are specifically needed by an agent running on the platform.

Note: All Historian Agents require publish_depth_first_all to be set to True in order to capture data.

Registry Configuration File

Registry configuration files setup each individual point on a device. Typically this file will be in CSV format, but the exact format is driver specific. See the section for a particular driver for the registry configuration format. The following is a simple example of a MODBUS registry confugration file:

34 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Table 5.1: Catalyst 371 Reference Volttron Units Units Modbus WritablePoint De- Notes Point Point Details Register Ad- fault Name Name dress Value CO2Sensor Retur- PPM 0.00- >f FALSE 1001 CO2 Reading nAirCO2 2000.00 0.00-2000.0 ppm CO2Stpt Retur- PPM 1000.00 >f TRUE 1011 1000 Setpoint to enable nAirCO2Stpt (de- demand control fault) ventilation HeatCall2 HeatCall2 On on/off BOOL FALSE 1114 Status indicator of / heating stage 2 need Off

MODBUS Driver Configuration

Currently VOLTTRON only supports the MODBUS over TCP/IP protocol.

driver_config

There are three arguments for the driver_config section of the device configuration file: • device_address - IP Address of the device. • port - Port the device is listening on. Defaults to 502 which is the standard port for MODBUS devices. • slave_id - Slave ID of the device. Defaults to 0. Use 0 for no slave. Here is an example device configuration file: { "driver_config":{"device_address": "10.1.1.2", "port": 502, "slave_id":5}, "campus": "pnnl", "building": "isb2", "unit": "hvac1", "driver_type": "modbus", "registry_config":"/home/volttron-user/configs/hvac.csv", "interval": 60, "timezone": "UTC", "heart_beat_point": "heartbeat" }

A sample MODBUS configuration file can be found here or in the VOLTTRON repository in examples/configurations/drivers/modbus1.config

MODBUS Registry Configuration File

The registry configuration file is a CSV file. Each row configures a point on the device. The following columns are required for each row: • Volttron Point Name - The name by which the platform and agents running on the platform will refer to this point. For instance, if the Volttron Point Name is HeatCall1 (and using the example device configuration above)

5.3. VOLTTRON Driver Framework 35 VOLTTRON Documentation, Release 3.5RC1

then an agent would use pnnl/isb2/hvac1/HeatCall1 to refer to the point when using the RPC interface of the actuator agent. • Units - Used for meta data when creating point information on the historian. • Modbus Register - A string representing how to interpret the data register and how to read it it from the device. The string takes two forms: – “BOOL” for coils and discrete inputs. – A format string for the Python struct module. See http://docs.python.org/2/library/struct.html for full doc- umentation. The supplied format string must only represent one value. See the documentation of your device to determine how to interpret the registers. Some Examples:

* “>f” - A big endian 32-bit floating point number. * “l” - A big endian 32-bit integer. • Writable - Either “TRUE” or “FALSE”. Determines if the point can be written to. Only points labeled TRUE can be written to through the ActuatorAgent. • Point Address - Modbus address of the point. Cannot include any offset value, it must be the exact value of the address. The following column is optional: • Default Value - The default value for the point. When the point is reverted by an agent it will change back to this value. If this value is missing it will revert to the last known value not set by an agent. Any additional columns will be ignored. It is common practice to include a Point Name or Reference Point Name to include the device documentation’s name for the point and Notes and Unit Details for additional information about a point. The following is an example of a MODBUS registry confugration file:

36 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Table 5.2: Catalyst 371 Refer- Volttron Units Units Mod- WritablePoint De- Notes ence Point Details bus Ad- fault Point Name Regis- dress Value Name ter CO2Sensor Retur- PPM 0.00- >f FALSE1001 CO2 Reading nAirCO2 2000.00 0.00-2000.0 ppm CO2Stpt Retur- PPM 1000.00 >f TRUE 1011 1000 Setpoint to enable nAirCO2Stpt (default) demand control ventilation Cool1Spd CoolSup- % 0.00 to >f TRUE 1005 75 Fan speed on cool 1 call ply- 100.00 (75 FanSpeed1 default) Cool2Spd CoolSup- % 0.00 to >f TRUE 1007 90 Fan speed on Cool2 Call ply- 100.00 (90 FanSpeed2 default) Damper DamperSig- % 0.00 - >f FALSE1023 Output to the economizer nal 100.00 damper DaTemp Dis- F (-)39.99 to >f FALSE1009 Discharge air reading chargeAirTem- 248.00 perature ESMEcon- ESM- % 0.00 to >f TRUE 1013 5 Minimum damper Min Damper- 100.00 (5 position during the MinPosi- default) energy savings mode tion FanPower Supply- kW 0.00 to >f FALSE1015 Fan power from drive FanPower 100.00 FanSpeed Supply- % 0.00 to >f FALSE1003 Fan speed from drive FanSpeed 100.00 HeatCall1 HeatCall1 On on/off BOOL FALSE1113 Status indicator of / heating stage 1 need Off HeartBeat heartbeat On on/off BOOL FALSE1114 Status indicator of / heating stage 2 need Off A sample MODBUS registry file can be found here or in the VOLTTRON repository in examples/configurations/drivers/catalyst371.csv

BACnet Driver Configuration

Communicating with BACnet devices requires that the BACnet Proxy Agent is configured and running. All device communication happens through this agent. driver_config

There are six arguments for the “driver_config” section of the device configuration file: • device_address - Address of the device. If the target device is behind an IP to MS/TP router then Remote Station addressing will probably be needed for the driver to find the device. • device_id - BACnet ID of the device. Used to establish a route to the device at startup.

5.3. VOLTTRON Driver Framework 37 VOLTTRON Documentation, Release 3.5RC1

• min_priority - (Optional) Minimum priority value allowed for this device whether specifying the prioity man- ually or via the registry config. Violating this parameter either in the configuration or when writing to the point will result in an error. Defaults to 8. • max_per_request - (Optional) Configure driver to manually segment read requests. The driver will only grab up to the number of objects specified in this setting at most per request. This setting is primarily for scraping many points off of low resource devices that do not support segmentation. Defaults to 10000. • proxy_address - (Optional) VIP address of the BACnet proxy. Defaults to “platform.bacnet_proxy”. See Communicating With Multiple BACnet Networks for details. Unless your BACnet network has special needs you should not change this value. • ping_retry_interval - (Optional) The driver will ping the device to establish a route at startup. If the BACnet proxy is not available the driver will retry the ping at this interval until it succeeds. Defaults to 5. Here is an example device configuration file: { "driver_config":{"device_address": "10.1.1.3", "device_id": 500, "min_priority": 10, "max_per_request": 24 }, "campus": "pnnl", "building": "isb2", "unit": "vav", "driver_type": "bacnet", "registry_config":"/home/volttron-user/configs/vav.csv", "interval":5, "timezone": "UTC", "heart_beat_point": "heartbeat" }

A sample BACnet configuration file can be found here or in the VOLTTRON repository in examples/configurations/drivers/bacnet1.config

BACnet Registry Configuration File

The registry configuration file is a CSV file. Each row configures a point on the device. Most of the configuration file can be generated with the grab_bacnet_config.py utility in scripts/bacnet. See Automatically Generating BACnet Configuration Files. Currently the driver provides no method to access array type properties even if the members of the array are of a supported type. The following columns are required for each row: • Volttron Point Name - The name by which the platform and agents running on the platform will refer to this point. For instance, if the Volttron Point Name is HeatCall1 (and using the example device configuration above) then an agent would use “pnnl/isb2/hvac1/HeatCall1” to refer to the point when using the RPC interface of the actuator agent. • Units - Used for meta data when creating point information on the historian. • BACnet Object Type - A string representing what kind of BACnet standard object the point belongs to. Exam- ples include: – analogInput – analogOutput

38 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

– analogValue – binaryInput – binaryOutput – binaryValue – multiStateValue • Property - A string representing the name of the property belonging to the object. Usually this will be “present- Value”. • Writable - Either “TRUE” or “FALSE”. Determines if the point can be written to. Only points labeled TRUE can be written to through the ActuatorAgent. Points labeled “TRUE” incorrectly will cause an error to be returned when an agent attempts to write to the point. • Index - Object ID of the BACnet object. The following column is optional: • Write Priority - BACnet priority for writing to this point. Valid values are 1-16. Missing this column or leaving the column blank will use the default priority of 16. Any additional columns will be ignored. It is common practice to include a Point Name or Reference Point Name to include the device documentation’s name for the point and Notes and Unit Details” for additional information about a point.

5.3. VOLTTRON Driver Framework 39 VOLTTRON Documentation, Release 3.5RC1

Table 5.3: BACnet Point Name Volttron Point Units Unit BACnet Prop- WritableIn- Notes Name Details Object erty dex Type 2400Stevens/FCB.LocalPreheatTem- de- -50.00 to analogIn- pre- FALSE3000119Reso- Application.PH-T perature greesFahren- 250.00 put sent- lution: heit Value 0.1 2400Stevens/FCB.LocalReturnAirTem- de- -50.00 to analogIn- pre- FALSE3000120Reso- Application.RA-T perature greesFahren- 250.00 put sent- lution: heit Value 0.1 2400Stevens/FCB.LocalReturnAirHu- per- 0.00 to analogIn- pre- FALSE3000124Reso- Application.RA-H midity centRela- 100.00 put sent- lution: tiveHu- Value 0.1 midity 2400Stevens/FCB.LocalCoolingValve- percent 0.00 to analo- pre- TRUE 3000107Reso- Application.CLG-O OutputCom- 100.00 gOutput sent- lution: mand (default Value 0.1 0.0) 2400Stevens/FCB.LocalMixedAir- percent 0.00 to analo- pre- TRUE 3000110Reso- Application.MAD- DamperOut- 100.00 gOutput sent- lution: O putCommand (default Value 0.1 0.0) 2400Stevens/FCB.LocalPreheatValve- percent 0.00 to analo- pre- TRUE 3000111Reso- Application.PH-O OutputCom- 100.00 gOutput sent- lution: mand (default Value 0.1 0.0) 2400Stevens/FCB.LocalReheatValve- percent 0.00 to analo- pre- TRUE 3000112Reso- Application.RH-O OutputCom- 100.00 gOutput sent- lution: mand (default Value 0.1 0.0) 2400Stevens/FCB.LocalSupply- percent 0.00 to analo- pre- TRUE 3000113Reso- Application.SF-O FanSpeedOut- 100.00 gOutput sent- lution: putCommand (default Value 0.1 0.0) A sample BACnet registry file can be found here or in the VOLTTRON repository in examples/configurations/drivers/bacnet.csv

Fake Device Driver Configuration

This driver does not connect to any actual device and instead produces random and or pre-configured values. driver_config

There are no arguments for the “driver_config” section of the device configuration file. The driver_config entry must still be present and should be left blank Here is an example device configuration file: { "driver_config": {}, "campus": "pnnl", "building": "isb2",

40 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

"unit": "vav", "driver_type": "bacnet", "registry_config":"/home/volttron-user/configs/vav.csv", "interval":5, "timezone": "UTC", "heart_beat_point": "heartbeat" }

A sample fake device configuration file can be found here or in the VOLTTRON repository in examples/configurations/drivers/fake.config

Fake Device Registry Configuration File

The registry configuration file is a CSV file. Each row configures a point on the device. The following columns are required for each row: • Volttron Point Name - The name by which the platform and agents running on the platform will refer to this point. For instance, if the Volttron Point Name is HeatCall1 (and using the example device configuration above) then an agent would use pnnl/isb2/hvac1/HeatCall1 to refer to the point when using the RPC interface of the actuator agent. • Units - Used for meta data when creating point information on the historian. • Writable - Either “TRUE” or “FALSE”. Determines if the point can be written to. Only points labeled TRUE can be written to through the ActuatorAgent. Points labeled “TRUE” incorrectly will cause an error to be returned when an agent attempts to write to the point. The following columns are optional: • Starting Value - Initial value for the point. If the point is reverted it will change back to this value. By default points will start with a random value (1-100). • Type - Value type for the point. Defaults to “string”. Valid types are: – string – integer – float – boolean Any additional columns will be ignored. It is common practice to include a Point Name or Reference Point Name to include the device documentation’s name for the point and Notes and Unit Details for additional information about a point. Please note that there is nothing in the driver that will enforce anything specified in the Unit Details column.

5.3. VOLTTRON Driver Framework 41 VOLTTRON Documentation, Release 3.5RC1

Table 5.4: BACnet Volttron Point Units Units Details WritableStarting Type Notes Name Value Heartbeat On/Off On/Off TRUE 0 booleanPoint for heartbeat toggle Out- F -100 to 300 FALSE 50 float CO2 Reading 0.00-2000.0 ppm sideAirTemper- ature1 Sam- PPM 10.00 (default) TRUE 10 float Setpoint to enable demand pleWritable- control ventilation Float1 SampleLong1 Enu- 1 through 13 FALSE 50 int Status indicator of service switch mera- tion SampleWrita- % 0.00 to 100.00 TRUE 20 int Minimum damper position bleShort1 (20 default) during the standard mode SampleBool1 On / on/off FALSE TRUE booleanStatus indidcator of cooling Off stage 1 Sam- On / on/off TRUE TRUE booleanStatus indicator pleWritable- Off Bool1 A sample fake registry configuration file can be found here or in the VOLTTRON repository in examples/configurations/drivers/fake.csv

5.3.5 Data Collection Failover

Introduction

The failover system is an optional feature that extends the master driver. Similarly configured Volttron instances collect from the same device(s) for easy horizontal scaling. Instances will take turns scraping and publishing data so that each has more free cycles between scrapes. If one dies, those remaining won’t be interrupted. It can currently be found in the feature/failover branch.

| | ______| Collector 0 | ______| |----->|______|------>| | | DEVICE | ______| Target | |______|----->| |------>|______| | Collector 1 | |______|

Configuration

Redundant Volttron instances will have almost identical configurations. The following variables tell the drivers how periodic scrape intervals should be adjusted: • failover_array_size - The number of collecting instances. • failover_instance_id - Zero is the first instance. Below is an example of a master driver’s config file using the failover feature. This will be one instance in a pair.

42 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

{ "agentid":"master_driver", "failover_array_size":2, "failover_instance_id":0, # "failover_instance_id": 1,

"driver_config_list":["driver1.config","driver2.config"] }

To Do

• Automatically reschedule collection when an instance fails. • Dynamically add and remove instances from a collection array. This should be available in VC.

5.4 VOLTTRON Message Bus

5.4.1 VOLTTRON Interconnect Protocol

VIP Authentication

VIP (VOLTTRON Interconnect Protocol) authentication is implemented in the auth module (volt- tron/platform/auth.py) and extends the ZeroMQ Authentication Protocol ZAP to VIP by including the ZAP User-Id in the VIP payload, thus allowing peers to authorize access based on ZAP credentials. This document does not cover ZAP in any detail, but its understanding is fundamental to securely configuring ZeroMQ. While this document will attempt to instruct on securely configuring VOLTTRON for use on the Internet, it is recommended that the ZAP documentation also be consulted.

Default Encryption

By default, ZeroMQ operates in plain-text mode, without any sort of encryption. While this is okay for in-process and interprocess communications, via domain sockets, it is insecure for any kind of inter-network communications, especially when traffic must traverse the Internet. Therefore, VOLTTRON automatically generates an encryption key and enables CurveMQ by default on all TCP connections. The key is stored in $VOLTTRON_HOME/curve.key, where VOLTTRON_HOME defaults to $HOME/.volttron. The base64-encode public key is printed in the log output at the INFO level when VOLTTRON starts (with -v option) and should be used when connecting remote peers. Look for output like this: (volttron)[user@home]$ volttron -v 2015-09-01 12:15:05,334 () volttron.platform.main INFO: public key: 9yHEnRB_ct3lwpZi05CKtklzpXw26ehjwH-GBmfguRM

Peer Authentication

ZAP defines a method for verifying credentials exchanged when a connection is initially established. The authentica- tion mechanism provides three main pieces of information useful for authentication: • domain: a name assigned to a locally bound address (to which peers connect) • address: the remote address of the peer • credentials: includes the authentication method and any associated credentials

5.4. VOLTTRON Message Bus 43 VOLTTRON Documentation, Release 3.5RC1

During authentication, VOLTTRON checks these pieces against a list of accepted peers defined in a file, called the “auth file” in this document. This JSON-formatted file is located at $VOLTTRON_HOME/auth.json and must have a matching entry in the allow list for remote connections to be accepted. The file is initially generated automatically with a sample entry commented out. The file must consist of a dictionary with an “allow” key. The “allow” key should be a list of dictionaries containing auth entries. The only required key in those entries is “credentials” which is the colon-separated (:) concatenation of the auth mechanism (one of NULL, CURVE, or PLAIN) and the credentials. Optional keys in the auth entry are “domain” and “address”. Here are some examples:

Note: If using regular expressions in the “address” portion, denote this with “/”. Backslashes must be escaped “\”. This is a valid regular expression: "/192\\.168\\.1\\..*/" These are invalid: "/192\.168\.1\..*/", "/192\.168\.1\..*", "192\\.168\\.1\\..*" { "allow":[ {"credentials":"CURVE:wk2BXQdHkAlMIoXthOPhFOqWpapD1eWsBQYY7h4-bXw","domain":"vip","address":"/192 \\.168\\.1\\..*/"}, {"credentials":"/CURVE:. */","address":"127.0.0.1"}, {"credentials":"NULL","address":"/localhost:1000:1000:. */"} ] }

Each entry can be a string or a list of strings. Strings beginning and ending with forward slashes are considered regular expressions and match that way. Backward slashes must be escaped within this regular expression “\”. When authenticating, the credentials are checked. If they don’t exist or don’t match, authentication fails. Otherwise, if domain and address are not present (or are null), authentication succeeds. If address and/or domain exist, they must match as well for authentication to succeed. The NULL mechanism includes no credentials and is useful for IPC connections, which have an address like localhost:UID:GID:PID, where the latter parts are the peer’s user, group, and process IDs. CURVE creden- tials include the remote peer’s public key. Watching the INFO level log output of the auth module can help determine the required values for a specific peer. It is unnecessary to provide auth entries for local agents started by the platform as long as the agent continues to run with the same process ID it was assigned when launched. Child processes are not currently tracked, but that may change in the future.

Configuring Agents

A remote agent must know the platform’s public key (also called the server key) to successfully authenticate. The public key can be passed in via the remote address. VOLTTRON extends ZeroMQ’s address scheme by supporting URL-style parameters for configuration. The following parameters are supported when connecting: • serverkey: encoded public key of remote server • secretkey: agent’s own private/secret key • publickey: agent’s own public key • ipv6: instructs ZeroMQ to attempt to use IPv6 If either secretkey or publickey are missing and serverkey is defined, a temporary secret and public key will be au- tomatically generated. This will only work if the auth entry on the remote host is configured to allow such broad authentication. More permanent keypairs may be generated using the volttron-ctl keypair command.

44 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

(volttron)[brandon@deluxe platform]$ volttron-ctl keypair public: EIBCsV7PUngWJDgj0-lxSEjh7YigL6sLI-lvFN8oYVc secret: mgCegyw6CauL7EGifCPfxHSppdlC75MeGZ7O0VGN8og

Given the agent keys above and the platform public key from the first example log output above, the following address could be constructed to connect an agent: tcp://some.remote.volttron.server:5432?serverkey=9yHEnRB_ct3lwpZi05CKtklzpXw26ehjwH-GBmfguRM&publickey=EIBCsV7PUngWJDgj0-lxSEjh7YigL6sLI-lvFN8oYVc&secretkey=mgCegyw6CauL7EGifCPfxHSppdlC75MeGZ7O0VGN8og

The remote platform would require an auth entry similar to the following for the connection to succeed: {"credentials":"CURVE:EIBCsV7PUngWJDgj0-lxSEjh7YigL6sLI-lvFN8oYVc"}

Platform Configuration

By default, the platform only listens on the local IPC VIP socket. Additional addresses may be bound using the --vip-address option, which can be provided multiple times to bind multiple addresses. Each VIP address should follow the standard ZeroMQ convention of prefixing with the socket type (ipc:// or tcp://) and may include any of the following additional URL parameters: • server: ZAP mechanism; must be one of NULL, CURVE, or PLAIN (defaults to NULL for ipc:// and CURVE for tcp://) • domain: domain name to associate with this endpoint (defaults to “vip”) • secretkey: alternate private/secret key (defaults to generated key for tcp://) • ipv6: instructs ZeroMQ to attempt to use IPv6 If secretkey is provided without server, server is assumed to be CURVE.

Questions and Answers

• I really don’t like security or encrypting my important data. Can I disable the default TCP encryption? Yes, but we strongly advise against it for production deployments. Simply truncate the key file to zero bytes (truncate -s 0 $VOLTTRON_HOME/curve.key). • Can I temporarily disable encryption and authentication for testing or development? Yes. Simply use the --developer-mode option when launching VOLTTRON. • I am binding to the loopback address. Can I disable CURVE authentication just for that address? Yes. Just use an address like tcp://127.0.0.1:5432?server=NULL (?server=NULL being the key).

VIP Authorization Examples

Note: authorization is a work in progress, and its implementation is in ‘the develop branch ‘__. VIP authentication and authorization go hand in hand. When a peer authenticates to a VOLTTRON platform that peer is authorized to issue commands and run agents on that platform. VIP authorization is about giving a platform owner the ability to limit the capabilities of authenticated peers.

5.4. VOLTTRON Message Bus 45 VOLTTRON Documentation, Release 3.5RC1

Single Capability

Suppose a VOLLTRON platform owner wants to limit how peers can call a weather agent on their platform. The owner specifies in $VOLTTRON_HOME/auth.json that two users (Alice and Bobby) can authenticate to the platform, but only Alice is authorized to read the temperature: { "allow":[ {"user_id":"Alice","capabilities":["can_read_temp"],"credentials":"CURVE:abc...", }, {"user_id":"Bobby","credentials":"CURVE:xyz...", }, ] }

(The credentials are abbreviated to simplify the example.) In the weather agent, the authorization requirement is specified by using the RPC.allow decorator: @RPC.allow('can_read_temp') @RPC.export def temperature(): ...

Alice’s agents (i.e., agents that have been authenticated using Alice’s credentials) can call temperature via RPC, but if one of Bobby’s agents tries to call temperature they will get an exception: error: method "temperature" requires capabilities set([’can_read_temp’]), but capability list [] was provided

Multiple Capabilities

Expanding on the weather-agent example, the temperature method can require agents to have multiple capabilities: @RPC.allow(['can_read_temp','can_read_weather_data']) @RPC.export def temperature(): ...

This requires an agent to have both the can_read_temp and the can_read_weather_data capabilities. Mul- tiple capabilities can also be specified by using multiple RPC.allow decorators: @RPC.allow('can_read_temp') @RPC.allow('can_read_weather_data') @RPC.export def temperature(): ...

Full Example

See the StandAloneWithAuth agent for a full example.

Ideas for Improvement

Utilizing Roles

Rather than requiring agent-authors to specify individual capabilities, we should use roles to group multiple capabili- ties. To make this work we would need to add a new decorator (e.g., RPC.allow_roles). We would also need to

46 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

define a mapping of roles to sets of capabilities. For example, somewhere we would define a role: { "roles":[ {"agent_control":["install_agent","remove_agent"]}, {"admin":["install_agent","remove_agent","start","stop"]} ] }

Default Deny-All

Currently the default is to allow anyone to call RPC-exported methods that are not decorated with RPC.allow.A more secure default would be to disallow everyone (at least remote users) from calling methods that are not decorated with RPC.allow.

Authorize at the Agent Level

Authorization is designed to work with user/peer authentication. So if user Alice authenticates to a platform, then all of Alice’s agents are granted Alice’s capabilities. It would be nice to be able to selectively grant capabilities to individual agents.

VIP Enhancements

Outline a vision of how VOLTTRON Message Bus should work When creating VIP for VOLTTRON 3.0 we wanted to address two security concerns and one user request: • Security Concern 1: Agents can spoof each other on the VOLTTRON message bus and fake messages. • Security Concern 2: Agents can subscribe to topics that they are not authorized to subscribe to. • User Request 1: Several users requested means to transfer large amounts of data between agents without using the message bus. VOLTTRON Interconnect Protocol (VIP) was created to address these issues but unfortunately, it broke the easy to use pub-sub messaging model of VOLTTRON. Additionally to use the security features of VOLTTRON in 3.0 code has become an ordeal especially when multiple platforms are concerned. Finally, VIP has introduced the requirement for knowledge of specific other platforms to agents written by users in order to be able to communicate. The rest of this memo focuses on defining the way VOLTTRON message bus will work going forward indefinitely and should be used as the guiding principles for any future work on VIP and VOLTTRON. VOLTTRON Message Bus Guiding Principles: 1. All communications between two or more different VOLTTRON platforms MUST go through the VIP Router. Said another way, a user agent (application) should have NO capability to reach out to an agent on a different VOLTTRON platform directly. | All communications between two or more VOLTTRON platforms must be in the form of topics on the message bus. Agents MUST not use a distinct platform address or name to communicate via a direct connection between two platforms. 2. VOLTTRON will use two TCP ports. One port is used to extend VIP across platforms. A second port is used for the VOLTTRON discovery protocol (more on this to come on a different document). VIP will establish bi-directional communication via a single TCP port. 3. In order to solve the bootstrapping problem that CurveMQ has punted on, we will modify VIP to operate similar (behaviorally) to SSH.

5.4. VOLTTRON Message Bus 47 VOLTTRON Documentation, Release 3.5RC1

A. On a single VOLTTRON platform, the platform’s public key will be made available via an API so that all agents will be able to communicate with the platform. Additionally, the behavior of the platform will be changed so that agents on the same platform will automatically be added to auth.json file. No more need for user to add the agents manually to the file. The desired behavior is similar to how SSH handles known_hosts. Note that this behavior still addresses the security request 1 & 2. B. When connecting VOLTTRON platforms, VOLTTRON Discovery Protocol (VDP) will be used to discover the other platforms public key to establish the router to router connection. Note that since we BANNED agent to agent communication between two platforms, we have prevented an O(N^2) communication pattern and key bootstrapping problem. 1. Authorization determines what agents are allowed to access what topics. Authorization MUST be managed by the VOLTTRON Central platform on a per organization basis. It is not recommended to have different authorization profiles on different VOLTTRON instances belonging to the same organization. 2. VOLTTRON message bus uses topics such as and will adopt an information model agreed upon by the VOLT- TRON community going forward. Our initial information model is based on the OpenEIS schema going for- ward. A different document will describe the information model we have adopted going forward. All agents are free to create their own topics but the VOLTTRON team (going forward) will support the common VOLTTRON information model and all agents developed by PNNL will be converted to use the new information model. 3. Two connected VOLTTRON systems will exchange a list of available topics via the message router. This will allow each VIP router to know what topics are available at what VOLTTRON platform. 4. Even though each VOLTTRON platform will have knowledge of what topics are available around itself, no ac- tual messages will be forwarded between VOLTTRON platforms until an agent on a specific platform subscribes to a topic. When an agent subscribes to a topic that has a publisher on a different VOLTTRON platform, the VIP router will send a request to its peer routers so that the messages sent to that topic will be forwarded. There will be cases (such as clean energy transactive project) where the publisher to a topic may be multiple hops away. In this case, the subscribe request will be sent towards the publisher through other VIP routers. In order to find the most efficient path, we may need to keep track of the total number of hops (in terms of number of VIP routers). 5. The model described in steps 5/6/7 applies to data collection. For control applications, VOLTTRON team only allows control actions to be originated from the VOLTTRON instance that is directly connected to that controlled device. This decision is made to increase the robustness of the control agent and to encourage truly distributed applications to be developed. 6. Direct agent to agent communication will be supported by creation of an ephemeral topic under the topic hier- archy. Our measurements have shown repeatedly that the overhead of using the ZeroMQ message pub/sub is minimal and has zero impact on communications throughput. In summary, by making small changes to the way VIP operates, I believe that we can significantly increase the usability of the platform and also correct the mixing of two communication platforms into VIP. VOLTTRON message bus will return to being a pub/sub messaging system going forward. Direct agent to agent communication will be supported through the message bus.

VIP Known Identities

It is critical for systems to have known locations for receiving resources and services from in a networked environment. The following table details the vip identities that are reserved for volttron specific usage.

48 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

VIP Sphere of Notes Identity Influence platform Platform plat- Platform The PlatformAgent is responsible for this identity. It is used to allow the form.agent VolttronCentralAgent to control and individual platform. volt- Multi- The default identity for a VolttronCentralAgent. The PlatformAgent by default will use tron.central Network this as it’s manager, but can be overridden in the configuration file of individual agents. plat- platform An individual platform may have many historians available to it, however the only one form.historian that will be available through Volttron Central by default will be called this. Note that this does not require a specific type of historian, just that it’s VIP Identity. control platform Control is used to control the individual platform. From the command line when issuing any volttron-ctl operations or when using Volttron Central. pubsub platform Pub/Sub subsystem router plat- actuator Agent which coordinates sending control commands to devices. form.actuator

VIP - VOLTTRON™ Interconnect Protocol

This document specifies VIP, the VOLTTRON™ Interconnect Protocol. The use case for VIP is to provide commu- nications between agents, controllers, services, and the supervisory platform in an abstract fashion so that additional protocols can be built and used above VIP. VIP defines how peers connect to the router and the messages they ex- change. • Name: github.com/VOLTTRON/volttron/wiki/VOLTTRON-Interconnect-Protocol • Editor: Brandon Carpenter • State: draft • See also: ZeroMQ, ZMTP, CurveZMQ, ZAP

Preamble

Copyright (c) 2015 Battelle Memorial Institute All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project.

5.4. VOLTTRON Message Bus 49 VOLTTRON Documentation, Release 3.5RC1

This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Overall Design

What Problems does VIP Address? When VOLTTRON agents, controllers, or other entities needed to exchange data, they previously used the first generation pub/sub messaging mechanism and ad-hoc methods to set up direct connections. While the pub/sub messaging is easy to implement and use, it suffers from several limitations: • It requires opening two listening sockets: one each for publishing and subscribing. • There is no trivial way to prevent message spoofing. • There is no trivial way to enable private messaging • It is not ideal for peer-to-peer communications. These limitations have severe security implications. For improved security in VOLTTRON, the communications protocol must provide a method for secure data exchange that is intuitive and simple to implement and use. ZeroMQ already provides many of the building blocks to implement encrypted and authenticated communications over a shared socket. It already includes a socket type implementing the router pattern. What remains is a protocol built on ZeroMQ to provide a single connection point, secure message passing, and retain the ability for entities to come and go as they please. VIP is just that protocol, specifically targeting the limitations above.

Why ZeroMQ? Rather than reinvent the wheel, VIP makes use of many features already implemented in ZeroMQ, including ZAP and CurveMQ. While VIP doesn’t require the use of ZAP or CurveMQ, their use substantially improves security by encrypting traffic over public networks and limiting connections to authenticated peers. ZeroMQ also provides reliable transports with built-in framing, automatic reconnection, in-process zero-copy message passing, abstractions for underlying protocols, and so much more. While some of these features create other pain points, they are minimal compared with the effort of either reimplementing or cobbling together libraries.

VIP is a routing protocol VIP uses the ZeroMQ router pattern. Specifically, the router binds a ROUTER socket and peers connect using a DEALER or ROUTER socket. Unless the peer is connecting a single socket to multiple routers, using the DEALER socket is easiest, but there are instances where using a ROUTER is more appropriate. One must just exercise care to include the proper address envelope to ensure proper routing.

50 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Extensible Security VIP makes no assumptions about the security mechanisms used. It works equally well over encrypted or unencrypted channels. Any connection-level authentication and encryption is handled by ZAP. Message- level authentication can be implemented in the protocols and services using VIP or by utilizing message properties set in ZAP replies.

ZeroMQ Compatibility For enhanced security, VOLTTRON recommends libzmq version 4.1 or greater, however, most features of VIP are available with older versions. The following is an incomplete list of core features available with recent versions of libzmq. • Version 3.2: – Basic, unauthenticated, unencrypted routing – Use ZMQ_ROUTER_BEHAVIOR socket option instead of ZMQ_ROUTER_MANDATORY • Version 4.0: – Adds authentication and encryption via ZAP • Version 4.1: – Adds message properties allowing correlating authentication tokens to messages

Message Format and Version Detection VIP uses a simple, multi-frame format for its messages. The first one (for peers) or two (for router) frames contain the delivery address(es) and are follow immediately by the VIP signature VIP1. The first characters of the signature are used to match the protocol and the last character digit indicates the protocol version, which will be incremented as the protocol is revised. This allows for fail-fast behavior and backward compatibility while being simple to implement in any language supported by ZeroMQ.

Formal Specification

Architecture VIP defines a message-based dialog between a router that transfers data between peers. The router and peers SHALL communicate using the following socket types and transports: • The router SHALL use a ROUTER socket. • Peers SHALL use a DEALER or ROUTER socket. • The router SHALL bind to one or more endpoints using inproc, tcp, or ipc address types. • Peers SHALL connect to these endpoints. • There MAY be any number of peers.

Message Format A routing exchange SHALL consist of a peer sending a message to the router followed by the router receiving the message and sending it to the destination peer. Messages sent to the router by peers SHALL consist of the following message frames: • The recipient, which SHALL contain the socket identity of the destination peer. • The protocol signature, which SHALL contain the four octets “VIP1”. • The user id, which SHALL be an implementation-defined value. • The request id, which SHALL contain an opaque binary blob. • The subsystem, which SHALL contain a string. • The data, which SHALL be zero or more subsystem-specific opaque frames.

5.4. VOLTTRON Message Bus 51 VOLTTRON Documentation, Release 3.5RC1

Messages received from a peer by the router will automatically have a sender frame prepended to the message by the ROUTER socket. When the router forwards the message, the sender and recipient fields are swapped so that the recipient is in the first frame and the sender is in the second frame. The recipient frame is automatically stripped by the ROUTER socket during delivery. Peers using ROUTER sockets must prepend the message with an intermediary frame, which SHALL contain the identity of a router socket. Messages received from the router by peers SHALL consist of the following message frames: • The sender, which SHALL contain the socket identity of the source peer. • The protocol signature, which SHALL contain the four octets “VIP1”. • The user id, which MAY contain a UTF-8 encoded string. • The request id, which SHALL contain an opaque binary blob. • The subsystem, which SHALL contain a non-empty string. • The data, which SHALL be zero or more subsystem-specific opaque frames. The various fields have these meanings: • sender: the ZeroMQ DEALER or ROUTER identity of the sending (source) peer. • recipient: the ZeroMQ DEALER or ROUTER identity of the recipient (destination) peer. • intermediary: the ZeroMQ ROUTER identity of the intermediary router. • user id: VIP authentication metadata set in the authenticator. See the discussion below for more information on this value. • request id: the meaning of this field is defined by the sending peer. Replies SHALL echo the request id without modifying it. • subsystem: this specifies the peer subsystem the data is intended for. The length of a subsystem name SHALL NOT exceed 255 characters and MUST only contain ASCII characters. • data: provides the data for the given subsystem. The number of frames required is defined by each subsystem.

User ID The value in the user id frame depends on the implementation and the version of ZeroMQ. If ZAP is used with libzmq 4.1.0 or newer, peers should send an empty string for the user id and the ZAP authenticator will replace it with an authentication token which receiving peers may use to authorize access. If ZAP is not used or a version of libzmq is used which lacks support for retrieving the user id metadata, an authentication subsystem may be used to authenticate peers. The authentication subsystem SHALL provide peers with private tokens that must be sent with each message in the user id frame and which the router will substitute with a public token before forwarding. If the message cannot be authenticated, the user id received by peers SHALL be a zero-length string.

Socket Types Peers communicating via the router will typically use DEALER sockets and should not require addi- tional handling. However, a DEALER peer may only connect to a single router. Peers may use ROUTER sockets to connect to multiple endpoints, but must prepend the routing ID of the destination. When using a DEALER socket: • A peer SHALL not send in intermediary address. • A peer SHALL connect to a single endpoint. When using a ROUTER socket: • A peer SHALL prepend the intermediary routing ID of to the message frames. • A peer MAY connect to multiple endpoints.

52 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Routing Identities Routing identities are set on a socket using the ZMQ_IDENTITY socket option and MUST be set on both ROUTER and DEALER sockets. The following additional requirements are placed on the use of peer identities: • Peers SHALL set a valid identity rather than rely on automatic identity generation. • The router MAY drop messages with automatically generated identities, which begin with the zero byte (‘0’). A zero length identity is invalid for peers and is, therefore, unroutable. It is used instead to address the router itself. • Peers SHALL use a zero length recipient to address the router. • Messages sent from the router SHALL have a zero length sender address.

Error Handling

The documented default behavior of ZeroMQ ROUTER sockets when entering the mute state (when the send buffer is full) is to silently discard messages without blocking. This behavior, however, is not consistently observed. Quietly discarding messages is not the desired behavior anyway because it prevents peers from taking appropriate action to the error condition. • Routers SHALL set the ZMQ_SNDTIMEO socket option to 0. • Routers SHALL forward EAGAIN errors to sending peers. It is also the default behavior of ROUTER sockets to silently drop messages addressed to unknown peers. • Routers SHALL set the ZMQ_ROUTER_MANDATORY socket option. • Routers SHALL forward EHOSTUNREACH errors to sending peers, unless the recipient address matches the sender. Most subsystems are optional and some way of communicating unsupported subsystems to peers is needed. • The error code 93, EPROTONOSUPPORT, SHALL be returned to peers to indicate unsupported or unimple- mented subsystems. The errors above are reported via the error subsystem. Other errors MAY be reported via the error subsystem, but subsystems SHOULD provide mechanisms for reporting subsystem-specific errors whenever possible. An error message must contain the following: • The recipient frame SHALL contain the socket identity of the original sender of the message. • The sender frame SHALL contain the socket identity of the reporting entity, usually the router. • The request ID SHALL be copied from the from the message which triggered the error. • The subsystem frame SHALL be the 5 octets ‘error’. • The first data frame SHALL be a string representation of the error number. • The second data frame SHALL contain a UTF-8 string describing the error. • The third data frame SHALL contain the identity of the original recipient, as it may differ from the reporter. • The fourth data frame SHALL contain the subsystem copied from the subsystem field of the offending message.

Subsystems

Peers may support any number of communications protocols or subsystems. For instance, there may be a remote procedure call (RPC) subsystem which defines its own protocol. These subsystems are outside the scope of VIP and this document with the exception of the hello and ping subsystems.

5.4. VOLTTRON Message Bus 53 VOLTTRON Documentation, Release 3.5RC1

• A router SHALL implement the hello subsystem. • All peers and routers SHALL implement the ping subsystem.

The hello Subsystem The hello subsystem provides one simple RPC-style routine for peers to probe the router for version and identity information. A peer hello request message must contain the following: • The recipient frame SHALL have a zero length value. • The request id MAY have an opaque binary value. • The subsystem SHALL be the 5 characters “hello”. • The first data frame SHALL be the five octets ‘hello’ indicating the operation. A peer hello reply message must contain the following: • The sender frame SHALL have a zero length value. • The request id SHALL be copied unchanged from the associated request. • The subsystem SHALL be the 7 characters “hello”. • The first data frame SHALL be the 7 octets ‘welcome’. • The second data frame SHALL be a string containing the router version number. • The third data frame SHALL be the router’s identity blob. • The fourth data frame SHALL be the peer’s identity blob. The hello subsystem can help a peer with the following tasks: • Test that a connection is established. • Discover the version of the router. • Discover the identity of the router. • Discover the identity of the peer. • Discover authentication metadata. For instance, if a peer will use a ROUTER socket for its connections, it must first know the identity of the router. The peer might first connect with a DEALER socket, issue a hello, and use the returned identity to then connect the ROUTER socket.

The ping Subsystem The ping subsystem is useful for testing the presence of a peer and the integrity and latency of the connection. All endpoints, including the router, must support the ping subsystem. A peer ping request message must contain the following: • The recipient frame SHALL contain the identity of the endpoint to query. • The request id MAY have an opaque binary value. • The subsystem SHALL be the 4 characters “ping”. • The first data frame SHALL be the 4 octets ‘ping’. • There MAY be zero or more additional data frames containing opaque binary blobs. A ping response message must contain the following: • The sender frame SHALL contain the identity of the queried endpoint.

54 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

• The request id SHALL be copied unchanged from the associated request. • The subsystem SHALL be the 4 characters “ping”. • The first data frame SHALL be the 4 octets ‘pong’. • The remaining data frames SHALL be copied from the ping request unchanged, starting with the second data frame. Any data can be included in the ping and should be returned unchanged in the pong, but limited trust should be placed in that data as it is possible a peer might modify it against the direction of this specification.

Discovery VIP does not define how to discover peers or routers. Typical options might be to hard code the router address in peers or to pass it in via the peer configuration. A well known (i.e. statically named) directory service might be used to register connected peers and allow for discovery by other peers.

Example Exchanges

These examples show the messages as sent on the wire as sent or received by peers using DEALER sockets. The messages received or sent by peers or routers using ROUTER sockets will have an additional address at the start. We do not show the frame sizes or flags, only frame contents.

Example of hello Request This shows a hello request sent by a peer, with identity “alice”, to a connected router, with identity “router”. +-+ | | Empty recipient frame +-+----+ | VIP1 | Signature frame +-+----+ | | Empty user ID frame +-+----+ | 0001 | Request ID, for example "0001" +------++ | hello | Subsystem, "hello" in this case +------+ | hello | Operation, "hello" in this case +------+

This example assumes a DEALER socket. If a peer uses a ROUTER socket, it SHALL prepend an additional frame containing the router identity, similar to the following example. This shows the example request received by the router: +------+ | alice | Sender frame, "alice" in this case +-+-----+ | | Empty recipient frame +-+----+ | VIP1 | Signature frame +-+----+ | | Empty user ID frame +-+----+ | 0001 | Request ID, for example "0001" +------++ | hello | Subsystem, "hello" in this case +------+

5.4. VOLTTRON Message Bus 55 VOLTTRON Documentation, Release 3.5RC1

| hello | Operation, "hello" in this case +------+

This shows an example reply sent by the router: +------+ | alice | Recipient frame, "alice" in this case +-+-----+ | | Empty sender frame +-+----+ | VIP1 | Signature frame +-+----+ | | Empty authentication metadata in user ID frame +-+----+ | 0001 | Request ID, for example "0001" +------++ | hello | Subsystem, "hello" in this case +------+-+ | welcome | Operation, "welcome" in this case +-----+---+ | 1.0 | Version of the router +-----+--+ | router | Router ID, "router" in this case +------++ | alice | Peer ID, "alice" in this case +------+

This shows an example reply received by the peer: +-+ | | Empty sender frame +-+----+ | VIP1 | Signature frame +-+----+ | | Empty authentication metadata in user ID frame +-+----+ | 0001 | Request ID, for example "0001" +------++ | hello | Subsystem, "hello" in this case +------+-+ | welcome | Operation, "welcome" in this case +-----+---+ | 1.0 | Version of the router +-----+--+ | router | Router ID, "router" in this case +------++ | alice | Peer ID, "alice" in this case +------+

Example of ping Subsystem This shows a ping request sent by the peer “alice” to the peer “bob” through the router “router”. +-----+ | bob | Recipient frame, "bob" in this case +-----++ | VIP1 | Signature frame +-+----+ | | Empty user ID frame

56 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

+-+----+ | 0002 | Request ID, for example "0002" +------+ | ping | Subsystem, "ping" in this case +------+ | ping | Operation, "ping" in this case +------+-----+ | 1422573492 | Data, a single frame in this case (Unix timestamp) +------+

This shows the example request received by the router: +------+ | alice | Sender frame, "alice" in this case +-----+-+ | bob | Recipient frame, "bob" in this case +-----++ | VIP1 | Signature frame +-+----+ | | Empty user ID frame +-+----+ | 0002 | Request ID, for example "0002" +------+ | ping | Subsystem, "ping" in this case +------+ | ping | Operation, "ping" in this case +------+-----+ | 1422573492 | Data, a single frame in this case (Unix timestamp) +------+

This shows the example request forwarded by the router: +-----+ | bob | Recipient frame, "bob" in this case +-----+-+ | alice | Sender frame, "alice" in this case +------++ | VIP1 | Signature frame +-+----+ | | Empty authentication metadata in user ID frame +-+----+ | 0002 | Request ID, for example "0002" +------+ | ping | Subsystem, "ping" in this case +------+ | ping | Operation, "ping" in this case +------+-----+ | 1422573492 | Data, a single frame in this case (Unix timestamp) +------+

This shows the example request received by “bob”: +------+ | alice | Sender frame, "alice" in this case +------++ | VIP1 | Signature frame +-+----+ | | Empty authentication metadata in user ID frame +-+----+ | 0002 | Request ID, for example "0002"

5.4. VOLTTRON Message Bus 57 VOLTTRON Documentation, Release 3.5RC1

+------+ | ping | Subsystem, "ping" in this case +------+ | ping | Operation, "ping" in this case +------+-----+ | 1422573492 | Data, a single frame in this case (Unix timestamp) +------+

If “bob” were using a ROUTER socket, there would be an additional frame prepended to the message containing the router identity, “router” in this case. This shows an example reply from “bob” to “alice” +------+ | alice | Recipient frame, "alice" in this case +------++ | VIP1 | Signature frame +-+----+ | | Empty user ID frame +-+----+ | 0002 | Request ID, for example "0002" +------+ | ping | Subsystem, "ping" in this case +------+ | pong | Operation, "pong" in this case +------+-----+ | 1422573492 | Data, a single frame in this case (Unix timestamp) +------+

The message would make its way back through the router in a similar fashion to the request.

Reference Implementation

Reference VIP router: https://github.com/VOLTTRON/volttron/blob/master/volttron/platform/vip/router.py Reference VIP peer: https://github.com/VOLTTRON/volttron/blob/master/volttron/platform/vip/socket.py

5.4.2 Remote Procedure Calls

Remote procedure calls (RPC) is a new feature added with VOLTTRON 3.0. The new VOLTTRON Interconnect Protocol VIP introduced the ability to create new point-to-point protocols, called subsystems, enabling the implemen- tation of JSON-RPC 2.0. This provides a simple method for agent authors to write methods and expose or export them to other agents, making request-reply or notify communications patterns as simple as writing and calling methods.

Exporting Methods

The export() method, defined on the RPC subsystem class, is used to mark a method as remotely accessible. This export() method has a dual use. The class method can be used as a decorator to statically mark methods when the agent class is defined. The instance method dynamically exports methods, and can be used with methods not defined on the agent class. Each take an optional export name argument, which defaults to the method name. Here are the two export method signatures: Instance method: RPC.export(method, name=None)

58 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Class method: RPC.export(name=None)

And here is an example agent definition using both methods: from volttron.platform.vip import Agent, Core, RPC def add(a, b): '''Add two numbers and return the result''' return a+b class ExampleAgent(Agent): @RPC.export def say_hello(self, name): '''Build and return a hello string''' return 'Hello, %s!'% (name,)

@RPC.export('say_bye') def bye(self, name): '''Build and return a goodbye string''' return 'Goodbye, %s.'% (name,)

@Core.receiver('setup') def onsetup(self, sender, **kwargs): self.vip.rpc.export('add')

Calling exported methods

The RPC subsystem provides three methods for calling exported RPC methods.

RPC.call(peer, method, *args, **kwargs)

Call the remote method exported by peer with the given arguments. Returns a gevent AsyncResult object. RPC.batch(peer, requests)

Batch call remote methods exported by peer. requests must be an iterable of 4-tuples (notify, method, args, kwargs), where notify is a boolean indicating whether this is a notification or standard call, method is the method name, args is a list and kwargs is a dictionary. Returns a list of AsyncResult objects for any standard calls. Returns None if all requests were notifications.

RPC.notify(peer, method, *args, **kwargs)

Send a one-way notification message to peer by calling method without without returning a result. Here are some examples: self.vip.rpc.call(peer,'say_hello','Bob').get() results= self.vip.rpc.batch(peer, [(False,'say_bye','Alice', {}), (True,'later', [], {})]) self.vip.rpc.notify(peer,'ready')

Inspection

A list of methods is available by calling the inspect method. Additional information can be returned for any method by appending ‘.inspect’ to the method name. Here are a couple examples:

5.4. VOLTTRON Message Bus 59 VOLTTRON Documentation, Release 3.5RC1

self.vip.rpc.call(peer,'inspect') # Returns a list of exported methods self.vip.rpc.call(peer,'say_hello.inspect') # Return metadata on say_hello method

Implementation

See the rpc module for implementation details. Also see RPC by example for additional examples.

5.4.3 Messaging and Topics

Introduction

Agents in VOLTTRON(tm) communicate with each other using a publish/subscribe mechanism built on the Zero MQ Python library. This allows for great flexibility as topics can be created dynamically and the messages sent can be any format as long as the sender and receiver understand it. An agent with data to share publishes to a topic, then any agents interested in that data subscribe to that topic. While this flexibility is powerful, it also could also lead to confusion if some standard is not followed. The current conventions for communicating in the VOLTTRON are: • Topics and subtopics follow the format: topic/subtopic/subtopic • Subscribers can subscribe to any and all levels. Subscriptions to “topic” will include messages for the base topic and all subtopics. Subscriptions to “topic/subtopic1” will only receive messages for that subtopic and any children subtopics. Subscriptions to empty string (“”) will receive ALL messages. This is not recommended. – All agents should subscribe to the “platform” topic. This is the topic the VOLTTRON will use to send messages to agents, such as “shutdown”. Agents should set the “From” header. This will allow agents to filter on the “To” message sent back. This is especially useful for requests to the ArchiverAgent so agents do not receive replies not meant for their request.

Topics

In VOLTTRON

• platform - Base topic used by the platform to inform agents of platform events • platform/shutdown - General shutdown command. All agents should exit upon receiving this. Message content will be a reason for the shutdown • platform/shutdown_agent - This topic will provide a specific agent id. Agents should subscribe to this topic and exit if the id in the message matches their id. • devices - Base topic for data being published by drivers • datalogger - Base topic for agents wishing to record time series data • record - Base topic for agents to record data in an arbitrary format.

Controller Agent Topics

See the documentation for the [[ActuatorAgent]].

60 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

5.5 Service Agents

5.5.1 Actuator Agent

ActuatorAgent

This agent is used to manage write access to devices. Agents may request scheduled times, called Tasks, to interact with one or more devices. All of the Actuator Agent documentation is now in here: actuator-agent

Actuator Agent Communication

Scheduling and canceling a Task. Interacting with a device via the ActuatorAgent. AcutatorAgent responses to a schedule or cancel request. Schedule state announcements. What happens when a running Task is preempted. Setup heartbeat signal for a device. ActuatorAgent configuration. Notes on programming agents to work with the ActuatorAgent

Notes on Working With the ActuatorAgent

• An agent can watch the window value from device state updates to perform scheduled actions within a timeslot. – If an Agent’s Task is LOW_PREEMPT priority it can watch for device state updates where the window is less than or equal to the grace period (default 60.0). • When considering if to schedule long or multiple short time slots on a single device: – Do we need to ensure the device state for the duration between slots? – Yes. Schedule one long time slot instead. – No. Is it all part of the same Task or can we break it up in case there is a conflict with one of our time slots? • When considering time slots on multiple devices for a single Task: – Is the Task really dependent on all devices or is it actually multiple Tasks? • When considering priority: – Does the Task have to happen on an exact day? – No. Consider LOW and reschedule if preempted. – Yes. Use HIGH. – Is it problematic to prematurely stop a Task once started? – No. Consider LOW_PREEMPT and watch the device state updates for a small window value. – Yes. Consider LOW or HIGH.

5.5. Service Agents 61 VOLTTRON Documentation, Release 3.5RC1

• If an agent is only observing but needs to assure that no another Task is going on while taking readings it can schedule the time to prevent other agents from messing with a devices state. The schedule updates can be used as a reminder as to when to start watching. • Any device, existing or not, can be scheduled. This allows for agents to schedule fake devices to create reminders to start working later rather then setting up their own internal timers and schedules.

ActuatorAgent Configuration schedule_publish_interval:: Interval between ‘published schedule announcements ‘__ in seconds. Defaults to 30. | preempt_grace_time:: Minimum time given to Tasks which have been preempted to clean up in seconds. Defaults to 60. | schedule_state_file:: File used to save and restore Task states if the ActuatorAgent restarts for any reason. File will be created if it does not exist when it is needed.

Sample configuration file

{ “schedule_publish_interval”: 30, “schedule_state_file”: “actuator_state.pickle” }

Heartbeat Signal

The ActuatorAgent can be configured to send a heartbeat message to the device to indicate the platform is running. Ideally, if the heartbeat signal is not sent the device should take over and resume normal operation. The configuration has two parts, the interval (in seconds) for sending the heartbeat and the specific point that should be modified each iteration. The heart beat interval is specified with a global “heartbeat_interval” setting. The ActuatorAgent will automatically set the heartbeat point to alternating “1” and “0” values. Changes to the heartbeat point will be published like any other value change on a device. The heartbeat points are specified under “points” section of the configuration like so: "points": { "": { "heartbeat_point":"" } "": { "heartbeat_point":"" } }

Note: this requires that the device has been setup with a heartbeat support. For Catalyst controllers check with TWT if you’re not sure.

62 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Task Preemption

Both LOW and LOW_PREEMPT priority Tasks can be preempted. LOW priority Tasks may be preempted by a conflicting HIGH priority Task before it starts. LOW_PREEMPT priority Tasks can be preempted by HIGH priority Tasks even after they start. When a Task is preempted the ActuatorAgent will publish to “devices/actuators/schedule/response” with the following header: #python { 'type': 'CANCEL_SCHEDULE', 'requesterID': , 'taskID': }

And the message (after parsing the json): #python { 'result': 'PREEMPTED', 'info': '', 'data': { 'agentID': , 'taskID': } }

Preemption Grace Time

If a LOW_PREEMPT priority Task is preempted while it is running the Task will be given a grace period to clean up before ending. For every device which has a current time slot the window of remaining time will be reduced to the grace time. At the end of the grace time the Task will finish. If the Task has no currently open time slots on any devices it will end immediately.

Requesting Schedule Changes

For information on responses see AcutatorAgent responses to a schedule or cancel requests. For 2.0 Agents using the pubsub interface: The actuator agent expects all messages to be JSON and will parse them accordingly. Use publish_json to send messages where possible. 3.0 agents using pubsub for scheduling and setting point values should publish python objects like normal.

Scheduling a Task

An agent can request a task schedule by publishing to the “devices/actuators/schedule/request” topic with the following header: #python { 'type': 'NEW_SCHEDULE', 'requesterID': , #The name of the requesting agent. 'taskID': , #The desired task ID for this task. It must be unique among all other scheduled tasks.

5.5. Service Agents 63 VOLTTRON Documentation, Release 3.5RC1

'priority': , #The desired task priority, must be 'HIGH', 'LOW', or 'LOW_PREEMPT' } with the following message: #python [ ["campus/building/device1", #First time slot. "2013-12-06 16:00:00", #Start of time slot. "2013-12-06 16:20:00"], #End of time slot. ["campus/building/device1", #Second time slot. "2013-12-06 18:00:00", #Start of time slot. "2013-12-06 18:20:00"], #End of time slot. ["campus/building/device2", #Third time slot. "2013-12-06 16:00:00", #Start of time slot. "2013-12-06 16:20:00"], #End of time slot. #etc... ]

Points on Task Scheduling • Everything in the header is required. • Task id and requester id (agentid) should be a non empty value of type string • A Task schedule must have at least one time slot. • The start and end times are parsed with dateutil’s date/time parser. The default string representation of a python datetime object will parse without issue. • Two Tasks are considered conflicted if at least one time slot on a device from one task overlaps the time slot of the other on the same device. • The end time of one time slot can be the same as the start time of another time slot for the same device. This will not be considered a conflict. For example, time_slot1(device0, time1, time2) and time_slot2(device0,time2, time3) are not considered a conflict • A request must not conflict with itself. • If something goes wrong see this failure string list for an explanation of the error.

Task Priorities HIGH:: This Task cannot be preempted under any circumstance. This task may preempt other conflicting preemptable Tasks.

LOW:: This Task cannot be preempted **once it has started**. A Task is considered started once the earliest time slot on any de- vice has been reached. This Task may not preempt other Tasks. LOW_PREEMPT:: This Task may be preempted at any time. If the Task is preempted once it has begun run- ning any current time slots will be given a grace period (configurable in the ActuatorAgent configuration file, de- faults to 60 seconds) before being revoked. This Task may not preempt other Tasks.

64 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Canceling a Task

A task may be canceled by publishing to the “devices/actuators/schedule/request” topic with the following header: #python { 'type': 'CANCEL_SCHEDULE', 'requesterID': , #The name of the requesting agent. 'taskID': , #The desired task ID for this task. It must be unique among all other scheduled tasks. }

Points on Task Canceling • The requesterID and taskID must match the original values from the original request header. • After a Tasks time has passed there is no need to cancel it. Doing so will result in a “TASK_ID_DOES_NOT_EXIST” error. • If something goes wrong see this failure string list for an explanation of the error.

ActuatorAgent Response

In response to a Task schedule request the ActuatorAgent will respond on the topic “devices/actuators/schedule/result” with the header: #python { 'type': <'NEW_SCHEDULE', 'CANCEL_SCHEDULE'> 'requesterID': , 'taskID': }

And the message (after parsing the json): #python { 'result': <'SUCCESS', 'FAILURE', 'PREEMPTED'>, 'info': , 'data': }

The ActuatorAgent may publish cancellation notices for preempted Tasks using the “PREEMPTED” result.

Preemption Data

Preemption data takes the form: #python { 'agentID': , 'taskID': }

Failure Reasons

In many cases the ActuatorAgent will try to give good feedback as to why a request failed.

5.5. Service Agents 65 VOLTTRON Documentation, Release 3.5RC1

General Failures

INVALID_REQUEST_TYPE:: Request type was not "NEW_SCHEDULE" or "CANCEL_SCHEDULE". MISSING_TASK_ID:: Failed to supply a taskID. MISSING_AGENT_ID:: AgentID not supplied.

Task Schedule Failures

TASK_ID_ALREADY_EXISTS:: The supplied taskID already belongs to an existing task. MISSING_PRIORITY:: Failed to supply a priority for a Task schedule request. INVALID_PRIORITY:: Priority not one of "HIGH", "LOW", or "LOW_PREEMPT". MALFORMED_REQUEST_EMPTY:: Request list is missing or empty. REQUEST_CONFLICTS_WITH_SELF:: Requested time slots on the same device overlap.

MALFORMED_REQUEST:: Reported when the request parser raises an unhandled exception. The exception name and info are appended to this info string.

CONFLICTS_WITH_EXISTING_SCHEDULES:: This schedule conflict with an existing schedules that it cannot preempt. The data item for the results will contain info about the conflicts in this form (after parsing json):

#python { '': { '': [ ["campus/building/device1", "2013-12-06 16:00:00", "2013-12-06 16:20:00"], ["campus/building/device1", "2013-12-06 18:00:00", "2013-12-06 18:20:00"] ] '':[...] } '': {...} }

Task Cancel Failures

TASK_ID_DOES_NOT_EXIST:: Trying to cancel a Task which does not exist. This error can also occur when trying to cancel a finished Task.

AGENT_ID_TASK_ID_MISMATCH:: A different agent ID is being used when trying to cancel a Task.

Schedule State Broadcast

Periodically the ActuatorAgent will publish the state of all currently used devices. For each device the ActuatorAgent will publish to an associated topic: #python 'devices/actuators/schedule/announce/'

66 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

With the following header: #python { 'requesterID': , 'taskID': 'window': }

The frequency of the updates is configurable with the “schedule_publish_interval” setting.

ActuatorAgent Interaction

Once an Task has been scheduled and the time slot for one or more of the devices has started an agent may interact with the device using the get and set topics. Both get and set are responded to the same way. See [#ActuatorReply Actuator Reply] below.

Getting values

While the sMap driver for a device should always be setup to periodically broadcast the state of a device you may want an up to the moment value for an actuation point on a device. To request a value publish a message to the following topic: #python 'devices/actuators/get//'

Setting Values

Value are set in a similar manner: To set a value publish a message to the following topic: #python 'devices/actuators/set//'

With this header: #python { 'requesterID': }

And the message contents being the new value of the actuator. The actuator agent expects all messages to be JSON and will parse them accordingly. Use publish_json to send messages where possible. This is significant for Boolean values especially.

Actuator Reply

#ActuatorReply The ActuatorAgent will reply to both get and set‘ on the value topic for an actuator: #python 'devices/actuators/value//'

5.5. Service Agents 67 VOLTTRON Documentation, Release 3.5RC1

With this header: #python { 'requesterID': }

With the message containing the value encoded in JSON.

Actuator Error Reply

If something goes wrong the ActuatorAgent will reply to both get and set‘ on the error topic for an actuator: #python 'devices/actuators/error//'

With this header: #python { 'requesterID': }

The message will be in the following form: #python { 'type': 'value': }

Common Error Types

LockError:: Returned when a request is made when we do not have permission to use a device. (Forgot to schedule, preempted and we did not handle the preemption message correctly, ran out of time in time slot, etc...) ValueError:: Message missing or could not be parsed as JSON. Other error types involve problem with communication between the ActuatorAgent and sMap.

5.5.2 VOLTTRON Central Management

VOLTTRON Central Management Agent

Agent Introduction

The VOLTTRON Central Agent (VCM) is responsible for controlling multiple VOLTTRON instances through a single interfaces. The VOLTTRON instances can be either local or remote. VCM leverages an internal VOLTTRON web server providing a interface to our JSON-RPC based web api. Both the web api and the interface are served through the VCM agent. There is a VOLTTRON Central Demo that will allow you to quickly setup and see the current offerings of the interface. VOLTTRON Central will allow you to • See a list of platforms being managed. • Add and remove platforms. • Install, start and stop agents to the registered platforms.

68 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

• Create dynamic graphs from the historians based upon points. • Execute functions on remote platforms.

Instance Configuration

In order for any web agent to be enabled, there must be a port configured to serve the content. The easiest way to do this is to create a config file in the root of your VOLTTRON_HOME directory. (to do this automatically see :ref:‘ VOLTTRON Config‘) The following is an example of the configuration file [volttron] vip-addres=tcp://127.0.0.1:22916 bind-web-address=http://127.0.0.1:8080

** Note the above configuration will open a discoverable port for the volttron instance. In addition, the opening of this web address allows you to serve both static as well as dynamic pages. Verify that the instance is serving properly by pointing your web browser to http://127.0.0.1:8080/discovery/

This is the required information for a VolttronCentralPlatform to be able to be managed.

VOLTTRON Central Manager Configuration

The following is the default configuration file for VOLTTRON Central { # The agentid is used during display on the VOLTTRON central platform # it does not need to be unique. "agentid":"volttron central",

# Authentication for users is handled through a naive password algorithm # Note in the following example the user and password are identical. # import hashlib # hashlib.sha512(password).hexdigest() where password is the plain text password. "users":{ "reader":{ "password":"2d7349c51a3914cd6f5dc28e23c417ace074400d7c3e176bcf5da72fdbeb6ce7ed767ca00c6c1fb754b8df5114fc0b903960e7f3befe3a338d4a640c05dfaf2d", "groups":[ "reader" ] }, "writer":{ "password":"f7c31a682a838bbe0957cfa0bb060daff83c488fa5646eb541d334f241418af3611ff621b5a1b0d327f1ee80da25e04099376d3bc533a72d2280964b4fab2a32", "groups":[ "writer" ] }, "admin":{ "password":"c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec", "groups":[ "admin" ] }, "dorothy":{

5.5. Service Agents 69 VOLTTRON Documentation, Release 3.5RC1

"password":"cf1b67402d648f51ef6ff8805736d588ca07cbf018a5fba404d28532d839a1c046bfcd31558dff658678b3112502f4da9494f7a655c3bdc0e4b0db3a5577b298", "groups":[ "reader, writer" ] } } }

Agent Execution

To start VOLTTRON Central first make sure the VOLTTRON instance is running. Next create/choose the config file to use. Finally from an activated shell in the root of the VOLTTRON repository execute # Arguments are package to execute, config file to use, tag to use as reference ./scripts/core/pack_install.sh services/core/VolttronCentral services/core/VolttronCentral/config vc

# Start the agent volttron-ctl start --tag vc

VOLTTRON Central Web Services Api Documentation

VOLTTRON Central (VC) is meant to be the hub of communcation within a cluster of VOLTTRON instances. VC exposes a JSON-RPC 2.0 based api that allows a user to control multple instances of VOLTTRON.

Why JSON-RPC

SOAP messaging is unfriendly to many developers, especially those wanting to make calls in a browser from AJAX environment. We have therefore have implemented a JSON-RPC API capability to VC, as a more JSON/JavaScript friendly mechanism.

How the API is Implemented

• All calls are made through a POST to /jsonrpc • All calls (not including the call to authenticate) will include an authorization token (a json-rpc extension).

JSON-RPC Request Payload All posted JSON payloads will look like the following block: { "jsonrpc": "2.0", "method": "method_to_invoke", "params":{ "param1name": "param1value", "param2name": "param2value" }, "id": "unique_message_id", "authorization": "server_authorization_token" }

As an alternative, the params can be an array as illistrated by the following:

70 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

{ "jsonrpc": "2.0", "method": "method_to_invoke", "params":[ "param1value", "param2value" ], "id": "unique_message_id", "authorization": "server_authorization_token" }

For full documentation of the Request object please see section 4 of the JSON-RPC 2.0 specification.

JSON-RPC Response Payload All responses shall have either an either an error response or a result response. The result key shown below can be a single instance of a json type, an array or a JSON object. A result response will have the following format: { "jsonrpc": "2.0", "result": "method_results", "id": "sent_in_unique_message_id" }

An error response will have the following format: { "jsonrpc": "2.0", "error":{ "code": "standard_code_or_extended_code", "message": "error message" } "id": "sent_in_unique_message_id_or_null" }

For full documenation of the Response object please see section 5 of the JSON-RPC 2.0 specification.

JSON-RPC Data Objects

header “Key”, “Type”, “Value” :widths: 10, 10, 40 “uuid”, “string”, “A unique identifier for the platform.” “name”, “string”, “A user defined string for the platform.” “status”, “Status”, “A status object for the platform.” header “Key”, “Type”, “Value” :widths: 10, 10, 40 “uuid”, “string”, “A unique identifier for the platform.” “name”, “string”, “A user defined string for the platform.” “status”, “Status”, “A status object for the platform.” header “Key”, “Type”, “Value” :widths: 10, 10, 40 “uuid”, “string”, “A unique identifier for the agent.” “name”, “string”, “Defaults to the agentid of the installed agent” “tag”, “string”, “A shortcut that can be used for referencing the agent” “priority”, “int”, “If this is set the agent will autostart on the instance.” “pro- cess_id”, “int”, “The process id or null if not running.” “status”, “string”, “A status string made by the status rpc call, on an agent.” header “Key”, “Type”, “Value” :widths: 10, 10, 40

5.5. Service Agents 71 VOLTTRON Documentation, Release 3.5RC1

“name”, “discovery_address”: header “Key”, “Type”, “Value” :widths: 10, 10, 40 “name”, “vip_address” header “Key”, “Type”, “Value” :widths: 10, 10, 40 “uuid”, “string”, “A unique identifier for the platform.” “name”, “string”, “A user defined string for the platform.” “status”, “Status”, “A status object for the platform.” header “Key”, “Type”, “Value” :widths: 10, 10, 40 “uuid”, “string”, “A unique identifier for the platform.” “name”, “string”, “A user defined string for the platform.” “status”, “Status”, “A status object for the platform.” header “Key”, “Type”, “Value” :widths: 10, 10, 40 “uuid”, “string”, “A unique identifier for the platform.” “name”, “string”, “A user defined string for the platform.” “status”, “Status”, “A status object for the platform.” header “Key”, “Type”, “Value” :widths: 10, 10, 40 “status”, “string”, “A value of GOOD, BAD, UNKNOWN, SUCCESS, FAIL” “context”, “string”, “Provides context about what the status means (optional)”

JSON-RPC API Methods

header “method”, “parameters”, “returns” :widths: 10, 10, 40 “get_authentication”, “(username, password)”, “authentication token”

Messages

Retrieve Authorization Token # POST /jsonrpc { "jsonrpc":"2.0", "method":"get_authorization", "params":{ "username":"dorothy", "password":"toto123" }, "id":"someID" }

Response Success # 200 OK { "jsonrpc":"2.0", "result":"somAuthorizationToken", "id":"someID" }

Failure HTTP Status Code 401 Register A Volttron Platform Instance (Using Discovery)

72 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

# POST /jsonrpc { "jsonrpc":"2.0", "method":"register_instance", "params":{ "discovery_address":"http://127.0.0.2:8080", "display_name":"foo" # Optional } "authorization":"someAuthorizationToken", "id":"someID" }

Success # 200 OK { "jsonrpc":"2.0", "result":{ "status":{ "code":"SUCCESS" "context":"Registered instance foo" # or the uri if not specified. } }, "id":"someID" }

TODO: Request Registration of an External Platform # POST /jsonrpc { "jsonrpc":"2.0", "method":"register_platform", "params":{ "uri":"127.0.0.2:8080?serverkey=...&publickey=...&secretkey=..." } "authorization":"someAuthorizationToken", "id": # }

Unregister a Volttron Platform Instance # POST /jsonrpc { "jsonrpc":"2.0", "method":"unregister_platform", "params":{ "platform_uuid":"somePlatformUuid", } "authorization":"someAuthorizationToken", "id":"someID" }

Retrieve Managed Instances #POST /jsonrpc { "jsonrpc":"2.0", "method":"list_platforms", "authorization":"someAuthorizationToken",

5.5. Service Agents 73 VOLTTRON Documentation, Release 3.5RC1

"id": # }

Response Success 200OK { "jsonrpc":"2.0", "result":[ { "name":"platform1", "uuid":"abcd1234-ef56-ab78-cd90-efabcd123456", "health":{ "status":"GOOD", "context": null, "last_updated":"2016-04-27T19:47:05.184997+00:00" } }, { "name":"platform2", "uuid":"0987fedc-65ba-43fe-21dc-098765bafedc", "health":{ "status":"BAD", "context":"Expected 9 agents running, but only 5 are", "last_updated":"2016-04-27T19:47:05.184997+00:00", }

}, { "name":"platform3", "uuid":"0000aaaa-1111-bbbb-2222-cccc3333dddd", "health":{ "status":"GOOD", "context":"Currently scraping 20 devices", "last_updated":"2016-04-27T19:47:05.184997+00:00", } } ], "id": # }

TODO: change repsonse Retrieve Installed Agents From platform1 # POST /jsonrpc { "jsonrpc":"2.0", "method":"platforms.uuid.abcd1234-ef56-ab78-cd90-efabcd123456.list_agents", "authorization":"someAuthorizationToken", "id": # }

Response Success 200OK { "jsonrpc":"2.0", "result":[ { "name":"HelloAgent",

74 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

"uuid":"a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6", "process_id": 3142, "error_code": null, "is_running": true, "permissions":{ "can_start": true, "can_stop": true, "can_restart": true, "can_remove": true } "health":{ "status":"GOOD", "context": null } }, { "name":"Historian", "uuid":"a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6", "process_id": 3143, "error_code": null, "is_running": true, "permissions":{ "can_start": true, "can_stop": true, "can_restart": true, "can_remove": true }

"health":{ "status":"BAD", "context":"No publish in last 5 minutes" } }, { "name":"VolltronCentralPlatform", "uuid":"a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6", "process_id": 3144, "error_code": null, "is_running": true, "permissions":{ "can_start": false, "can_stop": false, "can_restart": true, "can_remove": false } "health":{ "status":"BAD", "context":"One agent has reported bad status" } }, { "name":"StoppedAgent-0.1", "uuid":"a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6", "process_id": null, "error_code":0, "is_running": false,s "health":{ "status":"UNKNOWN",

5.5. Service Agents 75 VOLTTRON Documentation, Release 3.5RC1

"context":"Error code -15" } "permissions":{ "can_start": true, "can_stop": false, "can_restart": true, "can_remove": true } } ], "id": # }

TODO: Start An Agent # POST /jsonrpc { "jsonrpc":"2.0", "method":"platforms.uuid.0987fedc-65ba-43fe-21dc-098765bafedc.start_agent", "params":["a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6"], "authorization":"someAuthorizationToken", "id": # }

Response Success 200OK { "jsonrpc":"2.0", "result":{ "process_id": 1000, "return_code": null }, "id": # }

TODO: Stop An Agent # POST /jsonrpc { "jsonrpc":"2.0", "method":"platforms.uuid.0987fedc-65ba-43fe-21dc-098765bafedc.stop_agent", "params":["a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6"], "authorization":"someAuthorizationToken", "id": # }

Response Success 200OK { "jsonrpc":"2.0", "result":{ "process_id": 1000, "return_code":0 }, "id": # }

TODO: Remove An Agent

76 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

# POST /jsonrpc { "jsonrpc":"2.0", "method":"platforms.uuid.0987fedc-65ba-43fe-21dc-098765bafedc.remove_agent", "params":["a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6"], "authorization":"someAuthorizationToken", "id": # }

Response Success 200OK { "jsonrpc":"2.0", "result":{ "process_id": 1000, "return_code":0 }, "id": # }

TODO: Retrieve Running Agents # POST /jsonrpc { "jsonrpc":"2.0", "method":"platforms.uuid.0987fedc-65ba-43fe-21dc-098765bafedc.status_agents", "authorization":"someAuthorizationToken", "id": # }

Response Success 200OK { "jsonrpc":"2.0", "result":[ { "name":"RunningAgent", "uuid":"a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6" "process_id": 1234, "return_code": null }, { "name":"StoppedAgent", "uuid":"a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6" "process_id": 1000, "return_code":0 } ], "id": # }

TODO: currently getting 500 error Retrieve An Agent’s RPC Methods # POST /jsonrpc { "jsonrpc":"2.0", "method":"platforms.uuid.0987fedc-65ba-43fe-21dc-098765bafedc.agents.uuid.a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6.inspect", "authorization":"someAuthorizationToken",

5.5. Service Agents 77 VOLTTRON Documentation, Release 3.5RC1

"id": # }

Response Success 200OK { "jsonrpc":"2.0", "result":[ { "method":"sayHello", "params":{ "name":"string" } } ], "id": # }

TODO: Perform Agent Action # POST /jsonrpc { "jsonrpc":"2.0", "method":"platforms.uuid.0987fedc-65ba-43fe-21dc-098765bafedc.agents.uuid.a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6.methods.say_hello", "params":{ "name":"Dorothy" }, "authorization":"someAuthorizationToken", "id": # }

Success Response 200OK { "jsonrpc":"2.0", "result":"Hello, Dorothy!", "id": # }

TODO: Install Agent # POST /jsonrpc { "jsonrpc":"2.0", "method":"platforms.uuid.0987fedc-65ba-43fe-21dc-098765bafedc.install", "params":{ "files":[ { "file_name":"helloagent-0.1-py2-none-any.whl", "file":"data:application/octet-stream;base64,..." }, { "file_name":"some-non-wheel-file.txt", "file":"data:application/octet-stream;base64,..." }, ... ], }

78 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

"authorization":"someAuthorizationToken", "id": # }

Success Response 200OK { "jsonrpc":"2.0", "result":{ [ { "uuid":"a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6" }, { "error":"Some error message" }, ... ] }, "id": # }

5.5.3 Platform Agent

Platform Agent

Introduction

The Platform Agent allows communication from a VOLTTRON Central instance. Each VOLTTRON instance that is to be controlled through the VOLTTRON Central agent should have one and only one Platform Agent. The Platform Agent must have the VIP identity of platform.agent.

Configuration The minimal configuration (and most likely the only used) for a Platform Agent is as follows { # Agent id is used in the display on volttron central. "agentid":"Platform 1", }

The other options for the Platform Agent configuration can be found in the Platform Agent source directory.

5.5.4 Weather Service Agent

Weather Agent

The Weather Agent provides weather data retrieved from Weather Underground to agents. It provides an example of accessing an external resource and publishing it to the VOLTTRON Message Bus. It also provides an example of dividing data up into topics and for implementing the “all” topic at each level. The Weather agent retrieves data in two ways.

5.5. Service Agents 79 VOLTTRON Documentation, Release 3.5RC1

Usage

In the first, the agent periodically retrieves information about a specific area from weather underground and publishes current information periodically (default once an hour) to the message bus. The area is set in the agents configuration file. Agents that are interested in this information can subscribe to “weather//<field>” where and <field> are described in the topics section below. Agents may also subscribe to “weather/all” which will give them a hierarchical json document laid out according to the weather agent topics (as below) or “weather//all” which provides a json document with all of the fields for the weather topic specified. The second mode of operation is on demand. An agent may specifically request weather data for an area by post- ing a message on the “weather/request” topic. The agents message includes the identifying information about the area (region/city, or zip code) which is used in the request to Weather Underground. The Weather Agent replies on the “weather/response” topic (to avoid confusion with agents subscribing to the periodic posts). As with the first mode of operation, the agent making the request can subscribe to the “weather/response/all” subtopic, “weather/response//all”, or “weather/response//<field>” topics to receive the response data from the Weather Agent. Agents making requests must have the requesterID header set.

3.0 Agent Example

To make a request of the weather agent. Python 2.7.6 (default, Jun 22 2015, 17:58:13) [GCC 4.8.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> from volttron.platform.vip.agent import * >>> a = Agent() >>> import gevent >>> gevent.spawn(a.core.run).join(0) >>> a.vip.pubsub.publish('pubsub', 'weather/request', headers={'requesterID': 'agentid'}, message={'zipcode': '99336'}).get(timeout=10) headers={} headers[headers_mod.REQUESTER_ID]= agent_id msg={"zipcode":"99352"} self.publish_json('weather/request', headers, msg)

Make sure you get your own API Key from Weather Underground before using the weather agent.

Sample Output

Following is the response message that would be returned on “weather/all” or “weather/response/all”. { "cloud_cover":{ "UV":"6", "solarradiation":"", "visibility_km":"16.1", "visibility_mi":"10.0", "weather":"Clear" }, "location":{ "display_location":{ "city":"Richland", "country":"US", "country_iso3166":"US", "elevation":"121.00000000",

80 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

"full":"Richland, WA", "latitude":"46.28490067", "longitude":"-119.29721832", "state":"WA", "state_name":"Washington", "zip":"99352" }, "local_tz_long":"America/Los_Angeles", "observation_location":{ "city":"Richland, Richland", "country":"US", "country_iso3166":"US", "elevation":"397 ft", "full":"Richland, Richland, Washington", "latitude":"46.285866", "longitude":"-119.304375", "state":"Washington" }, "station_id":"KWARICHL21" }, "precipitation":{ "dewpoint_c":7, "dewpoint_f": 44, "dewpoint_string":"44 F (7 C)", "precip_1hr_in":"0.00", "precip_1hr_metric":"0", "precip_1hr_string":"0.00 in ( 0 mm)", "precip_today_in":"0.00", "precip_today_metric":"0", "precip_today_string":"0.00 in (0 mm)" }, "pressure_humidity":{ "pressure_mb":"1014", "pressure_trend":"-", "relative_humidity":"40%" }, "temperature":{ "feelslike_c":"20.6", "feelslike_f":"69.1", "feelslike_string":"69.1 F (20.6 C)", "heat_index_c":"NA", "heat_index_f":"NA", "heat_index_string":"NA", "temp_c": 20.6, "temp_f": 69.1, "temperature_string":"69.1 F (20.6 C)", "windchill_c":"NA", "windchill_f":"NA", "windchill_string":"NA" }, "time":{ "local_epoch":"1368724778", "local_time_rfc822":"Thu, 16 May 2013 10:19:38 -0700", "local_tz_offset":"-0700", "local_tz_short":"PDT", "observation_epoch":"1368724692", "observation_time":"Last Updated on May 16, 10:18 AM PDT", "observation_time_rfc822":"Thu, 16 May 2013 10:18:12 -0700"

5.5. Service Agents 81 VOLTTRON Documentation, Release 3.5RC1

}, "wind":{ "pressure_in":"29.94", "wind_degrees":3, "wind_dir":"North", "wind_gust_kph":"4.8", "wind_gust_mph":"3.0", "wind_kph": 2.7, "wind_mph": 1.7, "wind_string":"From the North at 1.7 MPH Gusting to 3.0 MPH" } }

For a more comprehensive listing of Weather Agent subtopics see WeatherAgentTopics

Weather Agent Topics

Topics used by the WeatherAgent with example output. [’weather/all’, ‘{“temperature”: {“windchill_f”: “NA”, “temp_f”: 69.1, “heat_index_f”: “NA”, “heat_index_string”: “NA”, “temp_c”: 20.6, “feelslike_c”: “20.6”, “windchill_string”: “NA”, “feelslike_f”: “69.1”, “heat_index_c”: “NA”, “windchill_c”: “NA”, “feelslike_string”: “69.1 F (20.6 C)”, “temperature_string”: “69.1 F (20.6 C)”}, “cloud_cover”: {“visibility_mi”: “10.0”, “solarradiation”: “”, “weather”: “Clear”, “visibility_km”: “16.1”, “UV”: “6”}, “location”: {“display_location”: {“city”: “Richland”, “full”: “Richland, WA”, “elevation”: “121.00000000”, “state_name”: “Washington”, “zip”: “99352”, “country”: “US”, “longitude”: “-119.29721832”, “state”: “WA”, “country_iso3166”: “US”, “latitude”: “46.28490067”}, “local_tz_long”: “America/Los_Angeles”, “observation_location”: {“city”: “Richland, Richland”, “full”: “Richland, Richland, Washington”, “elevation”: “397 ft”, “country”: “US”, “longi- tude”: “-119.304375”, “state”: “Washington”, “country_iso3166”: “US”, “latitude”: “46.285866”}, “station_id”: “KWARICHL21”}, “time”: {“local_tz_offset”: “-0700”, “local_epoch”: “1368724778”, “observation_time”: “Last Updated on May 16, 10:18 AM PDT”, “local_tz_short”: “PDT”, “observation_epoch”: “1368724692”, “lo- cal_time_rfc822”: “Thu, 16 May 2013 10:19:38 -0700”, “observation_time_rfc822”: “Thu, 16 May 2013 10:18:12 -0700”}, “pressure_humidity”: {“relative_humidity”: “40%”, “pressure_mb”: “1014”, “pressure_trend”: “-“}, “precipitation”: {“dewpoint_string”: “44 F (7 C)”, “precip_1hr_in”: “0.00”, “precip_today_in”: “0.00”, “pre- cip_today_metric”: “0”, “precip_today_string”: “0.00 in (0 mm)”, “dewpoint_f”: 44, “dewpoint_c”: 7, “pre- cip_1hr_string”: “0.00 in ( 0 mm)”, “precip_1hr_metric”: ” 0”}, “wind”: {“wind_degrees”: 3, “wind_kph”: 2.7, “wind_gust_mph”: “3.0”, “wind_mph”: 1.7, “wind_string”: “From the North at 1.7 MPH Gusting to 3.0 MPH”, “pressure_in”: “29.94”, “wind_dir”: “North”, “wind_gust_kph”: “4.8”}}’] [’weather/temperature/all’, ‘{“windchill_f”: “NA”, “temp_f”: 69.1, “heat_index_f”: “NA”, “heat_index_string”: “NA”, “temp_c”: 20.6, “feelslike_c”: “20.6”, “windchill_string”: “NA”, “feelslike_f”: “69.1”, “heat_index_c”: “NA”, “windchill_c”: “NA”, “feelslike_string”: “69.1 F (20.6 C)”, “temperature_string”: “69.1 F (20.6 C)”}’] [’weather/temperature/windchill_f’, ‘NA’] [’weather/temperature/temp_f’, ‘69.1’] [’weather/temperature/heat_index_f’, ‘NA’] [’weather/temperature/heat_index_string’, ‘NA’] [’weather/temperature/temp_c’, ‘20.6’] [’weather/temperature/feelslike_c’, ‘20.6’] [’weather/temperature/windchill_string’, ‘NA’] [’weather/temperature/feelslike_f’, ‘69.1’] [’weather/temperature/heat_index_c’, ‘NA’]

82 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

[’weather/temperature/windchill_c’, ‘NA’] [’weather/temperature/feelslike_string’, ‘69.1 F (20.6 C)’] [’weather/temperature/temperature_string’, ‘69.1 F (20.6 C)’] [’weather/cloud_cover/all’, ‘{“visibility_mi”: “10.0”, “solarradiation”: “”, “weather”: “Clear”, “visibility_km”: “16.1”, “UV”: “6”}’] [’weather/cloud_cover/visibility_mi’, ‘10.0’] [’weather/cloud_cover/solarradiation’, ‘’] [’weather/cloud_cover/weather’, ‘Clear’] [’weather/cloud_cover/visibility_km’, ‘16.1’] [’weather/cloud_cover/UV’, ‘6’] [’weather/location/all’, ‘{“display_location”: {“city”: “Richland”, “full”: “Richland, WA”, “elevation”: “121.00000000”, “state_name”: “Washington”, “zip”: “99352”, “country”: “US”, “longitude”: “-119.29721832”, “state”: “WA”, “country_iso3166”: “US”, “latitude”: “46.28490067”}, “local_tz_long”: “America/Los_Angeles”, “observation_location”: {“city”: “Richland, Richland”, “full”: “Richland, Richland, Washington”, “elevation”: “397 ft”, “country”: “US”, “longitude”: “-119.304375”, “state”: “Washington”, “country_iso3166”: “US”, “latitude”: “46.285866”}, “station_id”: “KWARICHL21”}’] [’weather/location/display_location/all’, ‘{“city”: “Richland”, “full”: “Richland, WA”, “elevation”: “121.00000000”, “state_name”: “Washington”, “zip”: “99352”, “country”: “US”, “longitude”: “-119.29721832”, “state”: “WA”, “country_iso3166”: “US”, “latitude”: “46.28490067”}’] [’weather/location/display_location/city’, ‘Richland’] [’weather/location/display_location/full’, ‘Richland, WA’] [’weather/location/display_location/elevation’, ‘121.00000000’] [’weather/location/display_location/state_name’, ‘Washington’] [’weather/location/display_location/zip’, ‘99352’] [’weather/location/display_location/country’, ‘US’] [’weather/location/display_location/longitude’, ‘-119.29721832’] [’weather/location/display_location/state’, ‘WA’] [’weather/location/display_location/country_iso3166’, ‘US’] [’weather/location/display_location/latitude’, ‘46.28490067’] [’weather/location/local_tz_long’, ‘America/Los_Angeles’] [’weather/location/observation_location/all’, ‘{“city”: “Richland, Richland”, “full”: “Richland, Richland, Washing- ton”, “elevation”: “397 ft”, “country”: “US”, “longitude”: “-119.304375”, “state”: “Washington”, “country_iso3166”: “US”, “latitude”: “46.285866”}’] [’weather/location/observation_location/city’, ‘Richland, Richland’] [’weather/location/observation_location/full’, ‘Richland, Richland, Washington’] [’weather/location/observation_location/elevation’, ‘397 ft’] [’weather/location/observation_location/country’, ‘US’] [’weather/location/observation_location/longitude’, ‘-119.304375’] [’weather/location/observation_location/state’, ‘Washington’]

5.5. Service Agents 83 VOLTTRON Documentation, Release 3.5RC1

[’weather/location/observation_location/country_iso3166’, ‘US’] [’weather/location/observation_location/latitude’, ‘46.285866’] [’weather/location/station_id’, ‘KWARICHL21’] [’weather/time/all’, ‘{“local_tz_offset”: “-0700”, “local_epoch”: “1368724778”, “observation_time”: “Last Updated on May 16, 10:18 AM PDT”, “local_tz_short”: “PDT”, “observation_epoch”: “1368724692”, “local_time_rfc822”: “Thu, 16 May 2013 10:19:38 -0700”, “observation_time_rfc822”: “Thu, 16 May 2013 10:18:12 -0700”}’] [’weather/time/local_tz_offset’, ‘-0700’] [’weather/time/local_epoch’, ‘1368724778’] [’weather/time/observation_time’, ‘Last Updated on May 16, 10:18 AM PDT’] [’weather/time/local_tz_short’, ‘PDT’] [’weather/time/observation_epoch’, ‘1368724692’] [’weather/time/local_time_rfc822’, ‘Thu, 16 May 2013 10:19:38 -0700’] [’weather/time/observation_time_rfc822’, ‘Thu, 16 May 2013 10:18:12 -0700’] [’weather/pressure_humidity/all’, ‘{“relative_humidity”: “40%”, “pressure_mb”: “1014”, “pressure_trend”: “-“}’] [’weather/pressure_humidity/relative_humidity’, ‘40%’] [’weather/pressure_humidity/pressure_mb’, ‘1014’] [’weather/pressure_humidity/pressure_trend’, ‘-‘] [’weather/precipitation/all’, ‘{“dewpoint_string”: “44 F (7 C)”, “precip_1hr_in”: “0.00”, “precip_today_in”: “0.00”, “precip_today_metric”: “0”, “precip_today_string”: “0.00 in (0 mm)”, “dewpoint_f”: 44, “dewpoint_c”: 7, “pre- cip_1hr_string”: “0.00 in ( 0 mm)”, “precip_1hr_metric”: ” 0”}’] [’weather/precipitation/dewpoint_string’, ‘44 F (7 C)’] [’weather/precipitation/precip_1hr_in’, ‘0.00’] [’weather/precipitation/precip_today_in’, ‘0.00’] [’weather/precipitation/precip_today_metric’, ‘0’] [’weather/precipitation/precip_today_string’, ‘0.00 in (0 mm)’] [’weather/precipitation/dewpoint_f’, ‘44’] [’weather/precipitation/dewpoint_c’, ‘7’] [’weather/precipitation/precip_1hr_string’, ‘0.00 in ( 0 mm)’] [’weather/precipitation/precip_1hr_metric’, ‘ 0’] [’weather/wind/all’, ‘{“wind_degrees”: 3, “wind_kph”: 2.7, “wind_gust_mph”: “3.0”, “wind_mph”: 1.7, “wind_string”: “From the North at 1.7 MPH Gusting to 3.0 MPH”, “pressure_in”: “29.94”, “wind_dir”: “North”, “wind_gust_kph”: “4.8”}’] [’weather/wind/wind_degrees’, ‘3’] [’weather/wind/wind_kph’, ‘2.7’] [’weather/wind/wind_gust_mph’, ‘3.0’] [’weather/wind/wind_mph’, ‘1.7’] [’weather/wind/wind_string’, ‘From the North at 1.7 MPH Gusting to 3.0 MPH’] [’weather/wind/pressure_in’, ‘29.94’]

84 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

[’weather/wind/wind_dir’, ‘North’] [’weather/wind/wind_gust_kph’, ‘4.8’]

5.6 VOLTTRON Security

Note: pubsub-topic protection is a work in progress, and its implementation is in ‘a feature branch ‘__. VIP authorization enables VOLTTRON platform owners to protect pub/sub topics. More specifically, a platform owner can limit who can publish to a given topic. This protects subscribers on that platform from receiving messages (on the protected topic) from unauthorized agents.

5.6.1 Example

To protect a topic, add the topic name to $VOLTTRON_HOME/protected_topics.json. For example, the following protected-topics file declares that the topic foo is protected: { "write-protect":[ {"topic":"foo","capabilities":["can_publish_to_foo"]} ] }

Note: The capability name can_publish_to_foo is not special. It can be any string, but it is easier to manage capabilities with meaningful names. Now only agents with the capability can_publish_to_foo can publish to the topic foo. To add this capability to authenticated agents, edit the file $VOLTTRON_HOME/auth.json: { "allow":[ {"user_id":"Alice","capabilities":["can_publish_to_foo"],"credentials":"CURVE:abc...", }, {"user_id":"Bobby","credentials":"CURVE:xyz...", }, ] }

(The credentials are abbreviated to simplify the example.) Alice’s agents (i.e., agents that have been authenticated using Alice’s credentials) can publish to topic foo. That is, Alice’s agents can call: self.vip.pubsub.publish('pubsub','foo', message='Here is a message')

Because Bobby’s agents do not have the necessary capabilities, if those agents try to publish to topic foo they will get an exception: to publish to topic "foo" requires capabilities [’can_publish_to_foo’], but capability list [] was provided

Regular Expressions

Topic names in $VOLTTRON_HOME/protected_topics.json can be specified as regular expressions. To an regular expression in the topic name begin and end the name with a “/”. For example:

5.6. VOLTTRON Security 85 VOLTTRON Documentation, Release 3.5RC1

{ "write-protect":[ {"topic":"/foo/ *.*/","capabilities":["can_publish_to_foo"]} ] }

This protects topics such as foo/bar and foo/anything.

5.6.2 Ideas for Improvement

Read Protection

Currently, pub/sub protection can only write-protect topics. This is useful when protecting the integrity of topic’s messages. To protect message confidentiality, we need a read-protection mechanism for pub/sub.

Usability

Currently, JSON files have to be manually edited to protect a pub/sub topic. It would be nice to have an interface in volttron-ctl and/or VOLTTRON Central for managing protected topics.

5.7 VOLTTRON PNNL Licensed Code

5.7.1 Agent Mobility

The mobility module can enable the deployment of agents within a site or to other sites (possibly in different building, cities, etc.) remotely. The mobility feature allows authorized VOLTTRON platforms to send and deploy agents, allowing for greater management and deployment ease and flexibility. This feature requires that you have installed the VOLTTRON™ Restricted. To create the required keys (minimum requirement to run VOLTTRON with Restricted module installed) enter the following commands in a command terminal: 1. Create ssh directory in VOLTTRON_HOME (see PlatformConfiguration for details on configuring the plat- form): mkdir -p ~/.volttron/ssh 2. Generate ssh key and add to id_rsa file: ssh-keygen -t rsa -N ’’ -f ~/.volttron/ssh/id_rsa 3. Create empty file for authorized keys and know hosts: touch ~/.volttron/ssh/{authorized_keys,known_hosts} Then, for each host you wish to authorize, its public key must be added to the authorized_keys file on the host to which it needs to connect. The public key has a .pub extension. The added hosts must have VOLTTRON instances installed, with the Restricted code installed and enabled (the authorized host has created the keys detailed in the steps above): • Copy host information securely: scp otherhost.example.com:~/.volttron/ssh/id_rsa.pub ./otherhost.pub • Append host key(s) to authorized_keys file in $VOLTTRON_HOME/ssh: ‘‘‘cat otherhost.pub >> ~/.volttron/ssh/authorized_keys‘‘‘‘

86 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

See the PingPongAgent example for information on how to add this feature to your custom agents.

5.7.2 Agent Package Signing

The creation of a signed agent package requires four certificates. The developer (creator) certificate is used to sign the agent code and allows the platform to verify that the agent code has not been modified since being distributed. The admin certificate is used for allowing the agent into a scope of influence. The initiator certificate is used when the agent is ready to be deployed into a specific platform. The platform certificate is used to sign the possibly modified data that an agent would like to carry with it during moving from platform to platform. All of these certificates must be signed by a “known” Certificate Authority (CA). In order to facilitate the development of agents Volttron Resticted includes packaging commands for creating the platform CA as well as the CA signed certificates for use in the agent signing process. When the Volttron Restricted package is installed on a platform the volttron-pkg command will be expanded to usage: volttron-pkg [-h] [-l FILE] [-L FILE] [-q] [-v] [–verboseness LEVEL] | {package,repackage,configure,create_ca,create_cert,sign,verify} The additional (sub)commands: • create_ca - Creates a platform specific root CA. When this command is executed the user will be required to respond to prompts in order to fill out the certificate’s data. • create_cert - Allows the creation of a ca signed certificate. A type of certificate must be specified as (–creator | –admin | –initiator | –platform) and the name(–name) of the certificate may be specified. The name will be used as the filename for the certificate on the platform. • sign - Signs the agent package at the specified level. • (ALWAYS REQUIRED) Agent package to be signed. • (ALWAYS REQUIRED) Signing level must be specified as one of (–creator | –admin | –initiator | –platform) and must be presented in the correct order. In other words an admin cannot sign the package until the creator has signed it. • –contract (resource contract) a file containing the definition of the necessary agent resources needed to execute properly. This option is only available to the creator. • –config-file a file used to define custom configuration for the starting of agent on the platform. This option is available to the initiator. • –certs_dir Allows the specification of where the certificate store is located. If this is not specified the default certificate store will be used. • verify Allows the user to verify a package is valid. • package - The agent package to validate against.

Agent Signing Example Using the Listener Agent

If VOLTTRON Restricted is installed and the security features are enabled, all agents must be signed prior to launching them. The following steps will describe how to sign an agent and will use the Listener agent as an example. From a terminal, in the volttron directory (~/volttron), enter the following commands: 1. Package the agent: volttron-pkg package examples/ListenerAgent

5.7. VOLTTRON PNNL Licensed Code 87 VOLTTRON Documentation, Release 3.5RC1

2. Sign the agent as creator: volttron-pkg sign --creator --contract resource_contract ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl 3. Sign the agent as admin: volttron-pkg sign --admin ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl 4. Sign the agent as initiator: volttron-pkg sign --initiator --config-file examples/ListenerAgent/config ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl 5. Set the configuration file: volttron-pkg configure ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl examples/ListenerAgent /config 6. Install agent into platform (with the platform running): volttron-ctl install ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl Upon successful completion of this command the terminal output will inform one of the install directory, the agent UUID (unique identifier for an agent, each instance of an agent will have a different UUID) and the agent name: Installed ~/.volttron/packaged/weatheragent-3.0-py2-none-any.whl as a9d67c55-7f58-4591-80af-3c1ff8a81740 listeneragent-3.0 Now the agent can be started, enter the following command to start the agent: volttron-ctl start --name listeneragent-3.0 or volttron-ctl start --uuid The previous steps document the signing of an agent using the Listener agent as an example. When deploying other agents the following parameters, used in the example, will need to be modified according to which agent you are signing and starting. The following is a brief description of the components of the commands in the signing example above: • examples/ListenerAgent - Path to agents top-level directory. • ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl - Path to the agent wheel (check the ~/.volt- tron/packaged directory after step 1 in this example to view the name of the wheel). • examples/ListenerAgent/config - Path to the agents json style configuration file (note this configuration file is named config).

5.7.3 Reource Monitor

The VOLTTRON™ Restricted additions provide additional protection against an agent consuming too many resources to the point of the host system becoming unresponsive or unstable. The resource monitor uses Linux control groups (or cgroups) to limit the CPU cycles and memory an individual agent may consume, preventing its possible overcon- sumption from adversely affecting other agents and services on the system. The execution requirements of an agent are set when provisioning an agent for service. When a request is made to move an agent to a new platform, part of the validation of the agent includes checking its execution requirements against resources currently available on the system. If the resources are available and the agent has passed all other validation, the agent will be executed and retain those resource guarantees throughout its lifetime on that platform. If the agent, however, requests memory or CPU cycles that are not available, its move request is denied and it will not execute on the requested platform.

88 Chapter 5. VOLTTRON Core Services VOLTTRON Documentation, Release 3.5RC1

Once an agent has been assigned resources, it is the responsibility of that agent to manage use of its resources. While an agent may exceed its resource guarantees when system utilization is low, when resources given to other agents are required, an agent exceeding the use in its contract may be terminated.

Execution Requirements

The execution requirements are specified as a JSON formatted document embedded in the agent during initial provi- sioning and takes the following form: { "requirements":{ "cpu.bogomips": 100, "memory.soft_limit_in_bytes": 2000000 } }

The contract must contain the requirements object, specifying the soft requirements, and might optionally specify a hard_requirements object. Each agent, including newly developed agents, must maintain their own requirements. The execution requirements for an agent are located in a file in the individual agent directory, called exereqs.json. For example, the execution requirements for the Listener Agent are located at volt- tron/examples/ListenerAgent/exereqs.json

Soft requirements

Soft requirements are considered soft on the platform because they change depending on the number of agents and other services are running on the system. They may also be negotiated on the fly in a future release. A list of the current resources which may be reserved follows: • cpu.bogomips - The CPU requirements of an agent indicated as either an exact integer (N >= 1) in MIPS (millions of instructions per second) or a floating-point percentage (0.0 < N < 1.0) of the total available bogo- MIPS on a system. Bogomips is a rough calculation performed at system boot indicating the likely number of calculations a system may perform each second. • memory.soft_limit_in_bytes - The maximum amount of random access memory (RAM) an agent requires to perform its tasks measured in bytes and given as an integer. Additional resources may be added in a future release.

Hard requirements

Hard requirements are based on system attributes that are very unlikely to change except after a system reboot. It is rare that an agent would need to set hard requirements and is usually only necessary for architecture-specific code. Each hard requirement is tested for a match. • kernel.name - Kernel name as given by uname. • kernel.release - Kernel release as given by uname. • kernel.version - Kernel version as given by uname. • architecture - Kernel architecture as given by uname. • os - Always ‘GNU/Linux’ • platform.version - Version of VOLTTRON in use.

5.7. VOLTTRON PNNL Licensed Code 89 VOLTTRON Documentation, Release 3.5RC1

• memory.total - Total amount of memory on the system in bytes. • bogomips.total - Total of all bogomips reported for all processors on the system.

Example using requirements

In this example we modify the execution requirements for the Listener Agent. 1. Open a terminal and type the following command: cat /proc/meminfo | grep MemTotal The output will be the total memory available on the system. Save this number. 2. In a text editor, open volttron/examples/ListenerAgent/exereqs.json 3. Replace the requirements with the following text: { "requirements":{ "cpu.bogomips": 100, "memory.soft_limit_in_bytes": 2000000 }, "hard_requirements":{ "os":"GNU/Linux", "memory.total": 2064328 } }

4. Replace the number for “memory.total” with the number from step 1, so that the requirement matches the memory for your system. 5. Save and close the file. Now, if the total memory on the system is changed, such as with a hardware update, the requirement will fail. Note that the hard requirements are separate, and follow the same format as the soft requirements.

90 Chapter 5. VOLTTRON Core Services CHAPTER 6

Developer Resources

6.1 Getting Started

6.1.1 Building VOLTTRON

The VOLTTRON project includes a bootstrap script which automatically downloads dependencies and builds VOLT- TRON. The script also creates a Python virtual environment for use by the project which can be activated after bootstrapping with ”. env/bin/activate”. This activated Python virtual environment should be used for subsequent bootstraps whenever there are significant changes. The system’s Python need only be used on the initial bootstrap. Before bootstrapping, ensure the required packages :ref: are installed. If you intend to develop in Eclipse, we recommend creating the work directory: ~/git or ~/workspace. Then run the following commands in the work directory to work with the master branch of the repository: git clone cd volttron

# bootstrap.py --help will show you all of the "package options" such as #installing required packages for volttron central or the platform agent. python2.7 bootstrap.py

For other options see: Getting VOLTTRON Note: Some packages (especially numpy) can be very verbose when they install. Please wait for the wall of text to finish. To test that installation worked, start up the platform in verbose mode and set a log file: # Activate the terminal source env/bin/activate

# Start the platform in the background volttron -vv -l volttron.log&

# If no errors are present then your setup is correct. You can # also tail the log file to see if the platform started correctly tail -f volttron.log

If you are developing in Eclipse, you should update the Python path at this point. See: Eclipse-Dev-Environment Note: The default working directory is ~/.volttron. The default directory for creation of agent packages is ~/.volt- tron/packaged To test agent deployment and messaging, build and deploy ListenerAgent. From the volttron directory:

91 VOLTTRON Documentation, Release 3.5RC1

# Activate the terminal source env/bin/activate

# Package the agent volttron-pkg package examples/ListenerAgent

# Set the agent's configuration file volttron-pkg configure ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl examples/ListenerAgent/config

# Install the agent (volttron must be running): volttron-ctl install ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl

# Start the agent: volttron-ctl start --name listeneragent-3.0

# Verify the agent has started volttron-ctl status

# Note the uuid # Check that Listener is publishing heartbeat message: cat volttron.log

# Stop the agent volttron-ctl stop --name listeneragent-3.0

# -- or -- volttron-ctl stop

See Speeding Up VOLTTRON Builds for information on improving VOLTTRON build times.

6.1.2 Speeding Up VOLTTRON™ Builds

The VOLTTRON build process is straightforward enough, but it can be a bit slow. It relies on pip to download, build, and install required third-party packages. The problem is that pip does very little to cache the results of builds, especially those which require compilation. In fact, the only thing pip caches is the downloaded source archives. While this speeds up the download process and lightens the burden of the Python package index (PyPi) server(s), it does little to improve the overall build speed. The majority of this document will focus on three techniques for improving VOLTTRON build times, including the pip download cache. But before we begin, let’s discuss what is involve in building VOLTTRON.

Introducing bootstrap.py

VOLTTRON can actually be built just like any other Python project. It includes a setup.py script in the project root so one can perform the standard build, install, sdist_*, and bdist_*, etc. commands. That’s great if we have some project which requires VOLTTRON, but that is not the typical use case for VOLTTRON. Usually, and especially for de- velopers, VOLTTRON is run in a virtual environment so its dependencies can be easily met. Enter bootstrap.py. Sitting next to setup.py in the project root is bootstrap.py, a script designed to bootstrap a virtual environment and make dependency installation a repeatable process. Bootstrapping occurs in two stages: download virtualenv, using it to create a virtual environment, and download and install dependencies. The first, or bootstrap, stage typically happens once. The second, or update, stage happens many times as dependencies are added or updated. It is the update stage that takes the majority of the time and is the stage we focus on in this document. To perform the bootstrap stage, bootstrap.py must be executed using the system Python interpreter.

92 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

$ python2.7 bootstrap.py

The above command assumes python2.7 is in the PATH and creates the virtual environment in the default env directory. After the virtual environment is created, the update stage is automatically started by executing bootstrap.py using the python interpreter in the newly created virtual environment. Subsequent updates must also use the interpreter in the virtual environment. $ env/bin/python bootstrap.py

Multiple options are available to alter the behavior of bootstrap.py. Use the --help option to list options and show the script’s usage message. $ env/bin/python bootstrap.py --help usage: bootstrap: python2.7 bootstrap.py [options] update: $VIRTUAL_ENV/bin/python bootstrap.py [options]

Bootstrap and update a virtual Python environment for VOLTTRON development.

optional arguments: -h, --help show this help message and exit -q, --quiet produce less output -v, --verbose produce extra output

bootstrap options: --envdir VIRTUAL_ENV alternate location for virtual environment --force force installing in non-empty directory -o, --only-virtenv create virtual environment and exit (skip install) --prompt PROMPT provide alternate prompt in activated environment (default: (volttron)) update options: -u, --upgrade upgrade installed packages -w, --wheel build wheels in the pip wheelhouse

The first invocation of this script, which should be made using the system Python, will create a virtual Python environment in the 'env' subdirectory in the same directory as this script or in the directory given by the --envdir option. Subsequent invocations of this script should use the Python executable installed in the virtual environment.

Enough about bootstrap.py. Let’s move on to the magic. As we do, please note the following about the output of commands: • Ellipses (...) are used to denote where excessive drivel was cut out to make this document shorter and more readable • Lines beginning with a plus (+) in bootstrap.py output show the actual calls to pip or easy_install, including all arguments. Oh, yeah. That reminds me that two packages require special handling. BACpypes must be installed using easy_install because it is only offered as an egg and pip doesn’t install from eggs. It will always be downloaded, if it isn’t already installed, and will not benefit from any of the speedups below. And pyzmq is handled separately to pass options to its setup.py. Note: As of 26 Feb 2015, BACpypes provides a wheel. Yay! Next step: Python 3. Okay. On with the show.

6.1. Getting Started 93 VOLTTRON Documentation, Release 3.5RC1

Preparation

Before building, we need to clone the VOLTTRON repository. We make sure to checkout the master branch to get the latest bootstrap script which has the special sauce for the real speed-up. [volttron@inamatus ~]$ git clone -b master https://github.com/VOLTTRON/volttron Cloning into 'volttron'... remote: Counting objects: 3268, done. remote: Compressing objects: 100% (122/122), done. Receiving objects: 100% (3268/3268), 14.25 MiB | 749.00 KiB/s, done. Resolving deltas: 100% (2070/2070), done. Checking connectivity... done.

Let’s move into that directory where the remainder of our time will be spent. [volttron@inamatus ~]$ cd volttron/

Now that we have the code, we are ready for testing. We’ll start with the slow method and work toward the fastest.

Meh (a.k.a. Slow Method)

Since version 6.0, pip caches downloaded source files to speed the download and install process when a package is once again required. The default location for this cache on Linux is in $HOME/.cache/pip (or ~/.cache/pip). As can be seen by the next command, we currently have no cache. [volttron@inamatus volttron]$ find ~/.cache/pip find: `/home/volttron/.cache/pip': No such file or directory

So let’s try bootstrapping the environment. We’ll use bash’s built-in time command to time the execution of each bootstrap command for comparison. [volttron@inamatus volttron]$ time python2.7 bootstrap.py Creating virtual Python environment Downloading virtualenv DOAP record Downloading virtualenv 12.0.7 New python executable in /home/volttron/volttron/env/bin/python2.7 Also creating executable in /home/volttron/volttron/env/bin/python Installing setuptools, pip...done. Installing required packages + easy_install BACpypes>=0.10,<0.11 ... + pip install --global-option --quiet --install-option --zmq=bundled --no-deps pyzmq>=14.3,<15 ... + pip install --global-option --quiet --editable ./lib/jsonrpc --editable . --requirement ./requirements.txt ... Successfully installed Smap-2.0.24c780d avro-1.7.7 configobj-5.0.6 ecdsa-0.13 flexible-jsonrpc gevent-1.0.1 greenlet-0.4.5 monotonic-0.1 numpy-1.9.1 pandas-0.15.2 paramiko-1.15.2 pycrypto-2.6.1 pymodbus-1.2.0 pyserial-2.7 python-dateutil-2.4.0 pytz-2014.10 requests-2.5.3 simplejson-3.6.5 six-1.9.0 twisted-15.0.0 volttron-2.0 wheel-0.24.0 zope.interface-4.1.2 real 9m2.299s user 7m51.790s sys 0m14.450s

Whew! The build took just over nine minutes on my nearly-4-year-old MacBook Pro running Arch Linux. In case you are wondering about my system’s name, as seen in the bash prompt, inamatus is Latin for unloved. I’ll leave it as an exercise for the user to determine why my system is unloved (hint: it has to do with a wonderful fruit with a bite missing from the side).

94 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Anyway, let’s have another look at the pip download cache. [volttron@inamatus volttron]$ find ~/.cache/pip -type f /home/volttron/.cache/pip/http/9/a/b/2/1/9ab21efc4225c8eb9aa41d1c76abef2a53babcefa438a79fa4e981ce /home/volttron/.cache/pip/http/9/2/6/7/2/92672ab99ac77960252018fbcb4f40984eef60ba5588229a729f18f5 /home/volttron/.cache/pip/http/9/e/6/1/9/9e61964f51d8a05a20ecf21eef694877f28cb654a123ce1316ff77e5 /home/volttron/.cache/pip/http/9/7/7/1/a/9771a6b64f3294ac335fdb8574cd3564e21c130924697381d72fd04d /home/volttron/.cache/pip/http/a/a/7/e/8/aa7e8bc2af1068a43747b0f771b426b7dcf7708283ca3ce3d92a2afc ... /home/volttron/.cache/pip/http/8/f/9/0/d/8f90d7cf09a2b5380a319b0df8eed268be28d590b6b5f71598a3b56f /home/volttron/.cache/pip/http/8/d/e/d/a/8deda849bcfd627b8587addf049f79bb333dd8fe1eae1d5053881039 /home/volttron/.cache/pip/http/8/8/7/a/6/887a67fb460d57a10a50deef3658834b9ac01722244315227d334628 /home/volttron/.cache/pip/http/5/5/4/e/2/554e2be8d96625aa74a4e0c4ee4a4b1ca10a442c2877bd3fff96e2a6 /home/volttron/.cache/pip/http/1/d/c/8/3/1dc83c11a861a2bc20d9c0407b41089eba236796ba80c213511f1f74 /home/volttron/.cache/pip/log/debug.log

The output is truncated because it was long and boring. The important thing is that it now exists. Next let’s remove the virtual environment and rebuild to see what effect the download cache has on our build time. [volttron@inamatus volttron]$ rm -rf env [volttron@inamatus volttron]$ time python2.7 bootstrap.py ... real 8m35.387s user 7m50.770s sys 0m14.170s

Notice that our CPU time was nearly the same, about 8 minutes (user + sys). So the remaining time was likely spent on I/O, which was reduced by about 30 seconds. We need something else to reduce CPU time. Enter ccache.

Better

What is ccache? According to the official ccache site, ccache is a compiler cache. It speeds up recompilation by caching the result of previous compilations and detecting when the same compilation is being done again. Sounds like just the thing we need. ccache is already properly configured on my system, it just needs to be placed early in the PATH to be found before the official gcc compilers. [volttron@inamatus volttron]$ which gcc /usr/bin/gcc [volttron@inamatus volttron]$ export PATH=/usr/lib/ccache/bin:$PATH [volttron@inamatus volttron]$ which gcc /usr/lib/ccache/bin/gcc

Now to prove to ourselves that the cache will be filled during the next run, let’s have a look at the cache status. [volttron@inamatus volttron]$ ccache -s cache directory /home/volttron/.ccache primary config /home/volttron/.ccache/ccache.conf secondary config (readonly) /etc/ccache.conf cache hit (direct) 0 cache hit (preprocessed) 0 cache miss 0 files in cache 0 cache size 0.0 kB max cache size 5.0 GB

6.1. Getting Started 95 VOLTTRON Documentation, Release 3.5RC1

The cache is indeed empty. Nothing up my sleeve... Presto! [volttron@inamatus volttron]$ rm -rf env [volttron@inamatus volttron]$ time python2.7 bootstrap.py ... real 6m3.496s user 4m57.960s sys 0m10.880s

One might expect a ccache build to take slightly longer than the baseline on the first build within a single project. This build completed about two minutes faster. Let’s look at the ccache status to discover why. [volttron@inamatus volttron]$ ccache -s cache directory /home/volttron/.ccache primary config /home/volttron/.ccache/ccache.conf secondary config (readonly) /etc/ccache.conf cache hit (direct) 204 cache hit (preprocessed) 23 cache miss 633 called for link 140 called for preprocessing 95 compile failed 1139 preprocessor error 4 bad compiler arguments 5 autoconf compile/link 103 no input file 19 files in cache 1316 cache size 26.1 MB max cache size 5.0 GB

Ah ha. There were a total of 227 cache hits, meaning that some of the files were identical across all the built packages and the cached version could be used rather than recompiling. Let’s see how subsequent builds improve with few cache misses. [volttron@inamatus volttron]$ rm -rf env [volttron@inamatus volttron]$ time python2.7 bootstrap.py ... real 3m15.811s user 2m24.890s sys 0m7.090s

Wow! Now we’re cooking with gas. Build times have been cut to nearly 1/3 of our baseline. This ccache status shows only 14 cache misses over our previous run: [volttron@inamatus volttron]$ ccache -s cache directory /home/volttron/.ccache primary config /home/volttron/.ccache/ccache.conf secondary config (readonly) /etc/ccache.conf cache hit (direct) 1038 cache hit (preprocessed) 35 cache miss 647 called for link 280 called for preprocessing 190 compile failed 2278 preprocessor error 8 bad compiler arguments 10

96 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

autoconf compile/link 206 no input file 38 files in cache 1365 cache size 35.0 MB max cache size 5.0 GB

So using ccache is a big win. Anyone compiling C or C++ on a Linux system should have ccache enabled. Wait, make that must. Go, now, and enable it on your Linux boxen. Or maybe finish reading this and then go do it. But do it!

Best

Now you’re thinking “how could it get any better,” right? Well, it can. What if those compiled packages only needed to be rebuilt when a new version was required instead of every time they are installed. When pip installs a package, it downloads the source and executes the packages setup.py like so: python setup.py install. The install command builds the package and installs it directly into the file system. What if we could package up the build results into an archive and just extract them to the file system when the package is installed. Enter wheel. pip supports the latest Python packaging format known as wheel. Typically this just means that it can install packages in the wheel format. However, if the wheel package is installed, pip can also build wheels from source, executing python setup.py bdist_wheel. By default, wheels are placed in the wheelhouse directory in the current working directory. But we can alter that location by setting an environment variable (read more on configuring pip here). [volttron@inamatus volttron]$ export PIP_WHEEL_DIR=$HOME/.cache/pip/wheelhouse

We also need to tell pip to look for the wheels, again using an environment variable. The directory needs to exist because while the wheel command will create the directory when creating the packages, pip may try to search the directory first. [volttron@inamatus volttron]$ export PIP_FIND_LINKS=file://$PIP_WHEEL_DIR [volttron@inamatus volttron]$ mkdir $PIP_WHEEL_DIR

So to get this all working, bootstrapping now has to occur in three steps: install the virtual environment, build the wheels, and install the requirements. bootstrap.py takes options that control its behavior. The first pass requires the -o or --only-virtenv option to stop bootstrap after installing the virtual environment and prevent the update stage. [volttron@inamatus volttron]$ rm -rf env [volttron@inamatus volttron]$ time python2.7 bootstrap.py --only-virtenv Creating virtual Python environment Downloading virtualenv DOAP record Downloading virtualenv 12.0.7 New python executable in /home/volttron/volttron/env/bin/python2.7 Also creating executable in /home/volttron/volttron/env/bin/python Installing setuptools, pip...done.

real 0m3.866s user 0m1.480s sys 0m0.230s

The second step requires the -w or --wheel option to build the wheels. Because the virtual environment already exists, bootstrap.py must be called with the virtual environment Python, not the system Python. [volttron@inamatus volttron]$ time env/bin/python bootstrap.py --wheel Building required packages + pip install --global-option --quiet wheel

6.1. Getting Started 97 VOLTTRON Documentation, Release 3.5RC1

... + pip wheel --global-option --quiet --build-option --zmq=bundled --no-deps pyzmq>=14.3,<15 ... + pip wheel --global-option --quiet --editable ./lib/jsonrpc --editable . --requirement ./requirements.txt ... Destination directory: /home/volttron/.cache/pip/wheelhouse Successfully built numpy pandas gevent monotonic pymodbus simplejson Smap greenlet pycrypto twisted pyserial configobj avro zope.interface real 3m15.431s user 2m17.980s sys 0m5.630s

It took 3.25 minutes to build the wheels (with ccache still enabled). Repeating this command results in nothing new being compiled and takes only 4 seconds. Only new versions of packages meeting the requirements will be built. [volttron@inamatus volttron]$ time env/bin/python bootstrap.py --wheel Building required packages ... Skipping numpy, due to already being wheel. Skipping pandas, due to already being wheel. Skipping python-dateutil, due to already being wheel. Skipping requests, due to already being wheel. Skipping flexible-jsonrpc, due to being editable Skipping pyzmq, due to already being wheel. Skipping gevent, due to already being wheel. Skipping monotonic, due to already being wheel. Skipping paramiko, due to already being wheel. Skipping pymodbus, due to already being wheel. Skipping setuptools, due to already being wheel. Skipping simplejson, due to already being wheel. Skipping Smap, due to already being wheel. Skipping wheel, due to already being wheel. Skipping volttron, due to being editable Skipping pytz, due to already being wheel. Skipping six, due to already being wheel. Skipping greenlet, due to already being wheel. Skipping ecdsa, due to already being wheel. Skipping pycrypto, due to already being wheel. Skipping pyserial, due to already being wheel. Skipping twisted, due to already being wheel. Skipping configobj, due to already being wheel. Skipping avro, due to already being wheel. Skipping zope.interface, due to already being wheel. real 0m3.998s user 0m3.580s sys 0m0.360s

And let’s see what is in the wheelhouse. [volttron@inamatus volttron]$ ls ~/.cache/pip/wheelhouse Smap-2.0.24c780d-py2-none-any.whl Twisted-15.0.0-cp27-none-linux_x86_64.whl avro-1.7.7-py2-none-any.whl configobj-5.0.6-py2-none-any.whl ecdsa-0.13-py2.py3-none-any.whl gevent-1.0.1-cp27-none-linux_x86_64.whl greenlet-0.4.5-cp27-none-linux_x86_64.whl

98 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

monotonic-0.1-py2-none-any.whl numpy-1.9.1-cp27-none-linux_x86_64.whl pandas-0.15.2-cp27-none-linux_x86_64.whl paramiko-1.15.2-py2.py3-none-any.whl pycrypto-2.6.1-cp27-none-linux_x86_64.whl pymodbus-1.2.0-py2-none-any.whl pyserial-2.7-py2-none-any.whl python_dateutil-2.4.0-py2.py3-none-any.whl pytz-2014.10-py2.py3-none-any.whl pyzmq-14.5.0-cp27-none-linux_x86_64.whl requests-2.5.3-py2.py3-none-any.whl setuptools-12.2-py2.py3-none-any.whl simplejson-3.6.5-cp27-none-linux_x86_64.whl six-1.9.0-py2.py3-none-any.whl wheel-0.24.0-py2.py3-none-any.whl zope.interface-4.1.2-cp27-none-linux_x86_64.whl

Now bootstrap.py can be run without options to complete the bootstrap process, again using the virtual environ- ment Python. [volttron@inamatus volttron]$ time env/bin/python bootstrap.py Installing required packages + easy_install BACpypes>=0.10,<0.11 ... + pip install --global-option --quiet --install-option --zmq=bundled --no-deps pyzmq>=14.3,<15 ... + pip install --global-option --quiet --editable ./lib/jsonrpc --editable . --requirement ./requirements.txt ... Successfully installed Smap-2.0.24c780d avro-1.7.7 configobj-5.0.6 ecdsa-0.13 flexible-jsonrpc gevent-1.0.1 greenlet-0.4.5 monotonic-0.1 numpy-1.9.1 pandas-0.15.2 paramiko-1.15.2 pycrypto-2.6.1 pymodbus-1.2.0 pyserial-2.7 python-dateutil-2.4.0 pytz-2014.10 requests-2.5.3 simplejson-3.6.5 six-1.9.0 twisted-15.0.0 volttron-2.0 zope.interface-4.1.2 real 0m11.137s user 0m8.930s sys 0m0.950s

Installing from wheels completes in only 11 seconds. And if we blow away the environment and bootstrap again, it takes under 15 seconds. [volttron@inamatus volttron]$ rm -rf env [volttron@inamatus volttron]$ time python2.7 bootstrap.py ... real 0m14.644s user 0m10.380s sys 0m1.240s

Building a clean environment now occurs in less than 15 seconds instead of the 9 minute baseline. That, my friends, is fast.

Why care?

The average VOLTTRON developer probably won’t care or see much benefit from the wheel optimization. The typical developer workflow does not include regularly removing the virtual environment and rebuilding. This is, however, very important for continuous integration (CI). With CI, a build server should check out a fresh copy of the source code, build it in a clean environment, and perform unit tests, notifying offending users when their changes break

6.1. Getting Started 99 VOLTTRON Documentation, Release 3.5RC1 things. Ideally, notification of breakage should happen as soon as possible. We just shaved nearly nine minutes off the turnaround time. It also reduces the load on a shared CI build server, which is nice for everyone.

Taking it further

Two additional use cases present themselves: offline installs and shared builds.

Offline Installs

Let’s say we have a system that is not connected to the Internet and, therefore, cannot download packages from PyPi or any other package index. Or perhaps it doesn’t have a suitable compiler. Wheels can be built on another similar, connected system and transferred by USB drive to the offline system, where they can then be installed. Note that the architecture must be identical and the OS must be very similar between the two systems for this to work. If the two systems differ too much for a compatible binary build and the offline system has a suitable compiler, then source files can be copied from the pip download cache and transferred from the online system to the offline system for building.

Shared Builds

If many developers are working on the same project, why not share the results of a build with the rest of the team? Here are some ideas to make it work: • Put wheels on a shared network drive • Run a private package index server (maybe with pypiserver) • Expose CI built wheels using Apache, Nginx, or SimpleHTTPServer

Issues

Here are some of the issues/drawbacks to the methods described above and some possible solutions. • Configuring pip using environment variables No worries. Pip uses configuration files too. And a benefit to using them is that it makes all these wheels available to other Python projects you may be working on, and vise versa. # /home/volttron/.config/pip/pip.conf [global] wheel-dir = /home/volttron/.cache/pip/wheelhouse find-links = file:///home/volttron/.cache/pip/wheelhouse

Find more on configuring pip here. • pip does not clean the wheelhouse This is not a deal-breaker. The wheel directory can just be removed and it will be recreated. Or a script can be used to remove all but the latest versions of packages. • Requires an additional step or two That’s the price for speed. But it can be mitigated by writing a script or bash alias to perform the steps.

100 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Conclusion

Here is a quick summary of the build times executed above: Method Time (minutes) Each builds on previous CPU Total baseline 8:07 9:02 with download cache 8:05 8:35 ccache, first run 5:09 6:03 ccache, subsequent runs 2:32 3:16 wheel, first run 2:35 3:30 wheel, subsequent runs 0:12 0:15 Not everyone cares about build times, but for those who do, pre-building Python wheels is a great way to improve install times. At a very minimum, every Python developer installing compiled packages will benefit from using ccache. The techniques used in this document aren’t just for VOLTTRON, either. They are generally useful for all moderately sized Python projects. If you haven’t installed ccache yet, go do it. There is no excuse.

6.1.3 Required Sofware: Linux

The following packages will need to be installed if they are not already: • git • build-essential • python-dev • openssl • libssl-dev • libevent-dev sudo apt-get update sudo apt-get install build-essential python-dev openssl libssl-dev libevent-dev git

If you have an agent which requires the pyodbc package, install the following: • freetds-bin • unixodbc-dev sudo apt-get install freetds-bin unixodbc-dev

Possible issues

The /tmp directory must allow exec. This error could manifest itself during the building of gevent. # Executing mount should have an entry like the following mount tmpfs on /tmp type tmpfs (rw,nosuid,nodev)

To change the mount you can use the following code

6.1. Getting Started 101 VOLTTRON Documentation, Release 3.5RC1

# remount /tmp to allow exec sudo mount -o remount,exec /tmp

# remount /tmp to disallow exec sudo mount -o remount,noexec /tmp

6.1.4 Repository Structure

There are several options for using the VOLTTRON code depending on whether you require the most stable version of the code or want the latest updates as they happen. In order of decreasing stability and increasing currency: For most stable, download the source code for the latest release at: https://github.com/VOLTTRON/volttron/releases These are purely source code and are not tied to the git repository. To update them will require downloading the newest source code and re-installing. For most stable but still pulling from the git repository use the 3.x branch (git clone –b 3.x https://github.com/VOLTTRON/volttron.git) The master branch is now the default branch for VOLTTRON (meaning this is what you clone if you do not use the “-b” option). This branch will get the latest stable features are they are pushed. Master and 3.x will remain in sync until 4.x is released. At this point, 3.x will stop being updated (except for fixes) and 4.x will track master. The “develop” branch contains the latest features as they are developed. Once a feature is considered “finished” it is merged back into develop. Develop will be merged into master once it is considered stable and ready for release. This branch can be cloned by those wanting to work from the latest version of the platform but should not be used in deployments. The least stable branches are the “feature” branches. These are branches contain features which are in progress and under development. It is not recommended to clone feature branches except for exploring a new feature.

6.1.5 VOLTTRON Restricted Code

Volttron Restricted adds a broader security layer on top of the volttron platform. If you are interested in this package please contact the volttron team at [email protected]. • NOTE: Once the package is installed all aspects of the package will be enforced. To override the behavior add no-verify, no-mobility, or no-resource-monitor to the configuration file. The Volttron Restricted package contains the following security enhancements: • The creation and usage of platform specific Certificate Authority (CA) certificates. • Multi-level signing of agent packages. • Multi-level verification of signed packages during agent execution. • Command line and agent based mobility. • Allows developer to customize an execution contract for required resources on the current and move requested platform. The following pages describe the functionality exposed by the Volttron Restricted package: • Signing and Verification of Agent Packages • Resource Monitor • PingPongAgent

102 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Note: VOLTTRON-Restricted supports VOLTTRON 2.x and is being update to VOLTTRON 3.5 Installation — VOLTTRON-Restricted requires a software development tool called SWIG (>=2.0.4). To install VOLTTRON- Restricted, follow the steps below. Enter all terminal commands from the VOLTTRON directory. • Extract the VOLTTRON-Restricted code to a new directory. These steps assume the location is ~/volttron-restricted • Install VOLTTRON-Restricted dependency: sudo apt-get install swig • Activate the VOLTTRON Platform (note the space after the period): . env/bin/activate • Install VOLTTRON-Restricted: pip install -e ~/volttron-restricted

devguides/setup/files/install-volttron-restricted.png

6.2 Agent Development

6.2.1 Agent Creation Walkthrough

It is recommended that developers look at the ListenerAgent before developing their own agent. That agent expresses the basic functionality this example will walk through and being familiar with the concepts will be useful. Full versions of the files discussed below are at: TestAgent Additional details about the commands used in this walkthrough are at: AgentManagement

Create Folders

• In the “applications” directory, create a new directory called TestAgent. • In TestAgent, create a new folder tester, this is the package where our python code will be created

Create Agent Code

• In tester, create a file called __init__.py which tells Python to treat this folder as a package • In the tester package folder, create the file agent.py • Create a class called TestAgent – Import the packages and classes we will need:

6.2. Agent Development 103 VOLTTRON Documentation, Release 3.5RC1

import sys

from volttron.platform.vip.agent import Agent, PubSub from volttron.platform.agent import utils

• This agent will extend BaseAgent to get all the default functionality – Since we want to publish we will import the PubSub module class TestAgent(Agent):

• Create an init method to deal with creating the agent and getting the config file later

def __init__(self, config_path, **kwargs): super(TestAgent, self).__init__(**kwargs)

Setting up a Subscription

We will set our agent up to listen to heartbeat messages (published by ListenerAgent). Using the PubSub decorator, we declare we want to match all topics which start with “heartbeat/listeneragent”. This will give us all heartbeat messages from all listeneragents but no others. @PubSub.subscribe('pubsub','heartbeat/listeneragent') def on_heartbeat_topic(self, peer, sender, bus, topic, headers, message): print "TestAgent got\nTopic: {topic}, {headers}, Message: {message}".format(topic=topic, headers=headers, message=message)

Argument Parsing and Main

Our agent will need to be able to parse arguments being passed on the command line by the agent launcher. Use the utils.default-main method to handle argument parsing and other default behavior. Create a main method which can be called by the launcher. def main(argv=sys.argv): '''Main method called by the platform.''' utils.vip_main(TestAgent)

if __name__ =='__main__': # Entry point for script try: sys.exit(main()) except KeyboardInterrupt: pass

Create Support Files for Agent

Volttron agents need some configuration files for packaging, configuration, and launching.

Packaging Configuration

In the TestAgent folder, create a file called “setup.py” (or copy the setup.py in ListenerAgent) which the platform will use to create a [wheel].(https://pypi.python.org/pypi/wheel). This file sets up the name, version, required packages, method to execute, etc. for the agent. The packaging process will also use this information to name the resulting file.

104 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

from setuptools import setup, find_packages

packages= find_packages('.') package= packages[0]

setup( name= package+'agent', version="0.1", install_requires=['volttron'], packages= packages, entry_points={ 'setuptools.installation':[ 'eggsecutable ='+ package+'.agent:main', ] } )

Launch Configuration

In TestAgent, create a file called “testagent.config”. This is the file the platform will use to launch the agent. It can also contain configuration information for the agent. For TestAgent, { "agentid":"Test1", "message":"hello" }

Agent Directory

At this point, the contents of the TestAgent directory should look like: applications/TestAgent/ -- setup.py -- testagent.config -- tester -- agent.py -- __init__.py

Packaging Agent

The agent code must now be packaged up for use by the platform. The package command will build the Python wheel using the setup.py file we defined earlier. From the project directory, activate the VOLTTRON environment with: . env/bin/activate Then call: volttron-pkg package applications/TestAgent By default, this creates a wheel file in the VOLTTRON_HOME directory (~/.volttron by default) in the packaged dreictory. Next, we add our configuration file to this package with:

6.2. Agent Development 105 VOLTTRON Documentation, Release 3.5RC1

volttron-pkg configure ~/.volttron/packaged/testeragent-0.1-py2-none-any.whl applications/TestAgent/testagent.config

Installing the Agent

Now we must install it into the platform. Use: volttron-ctl install ~/.volttron/packaged/testeragent-0.1-py2-none-any.whl

Testing the Agent

From the Command Line

To test the agent, we will start the platform, launch the agent, and check the log file. • With the VOLTTRON environment activated, start the platform by running volttron -l volttron.log -vv& • Launch the agent by running: volttron-ctl start --name testeragent-0.1 • Check that it is running: volttron-ctl status • Start the ListenerAgent as in BuildingTheProject • Check the log file for messages indicating the TestAgent is receiving the ListenerAgents messages: tail volttron.log

2014-09-17 15:30:50,088 (testeragent-0.1 3792) INFO: Topic: heartbeat/listeneragent, Headers({u'Date': u'2014-09-17 22:30:50.079548Z', u'AgentID': u'listener1', u'Content-Type': u'text/plain'}), Message: ['2014-09-17 22:30:50.079548Z']

In Eclipse

• If you are working in Eclipse, create a run configuration for TestAgent based on the ListenerAgent configuration in EclipseDevEnvironment. • Launch the platform • Launch the TestAgent • Launch the ListenerAgent TestAgent should start receiving the heartbeats from ListenerAgent

6.2.2 Agent Development Cheat Sheet

This is a catalogue of features available in volttron that are frequently useful in agent development.

Utilities

These functions can be found in the volttron.platform.agent.utils module. logging also needs to be imported to use the logger.

106 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

setup_logging

You’ll probably see the following lines near the top of agent files: utils.setup_logging() _log= logging.getLogger(__name__)

This code sets up the logger for this module so it can provide more useful output. In most cases it will be better to use the logger in lieu of simply printing messages with print. load_config load_config does just that. Give it the path to your config file and it will parse the json and return a dictionary. vip_main

This is the function that is called to start your agent. You’ll likely see it in the main methods at the bottom of agents’ files. Whatever is passed to it (a class name or a function that returns an instance of your agent) should accept a file path that can be parsed with load_config.

Core Agent Functionality

These tools volttron.platform.vip.agent module. Try importing

Agent Lifecycle Events

Each agent has four events that are triggered at different stages of its life. These are onsetup, onstart, onstop, and onfinish. Registering callbacks to these events are commonplace in agent development, with onstart being the most frequently used. The easiest way to register a callback is with a function decorator: @Core.receiver('onstart') def function(self, sender, **kwargs): function_body

Periodic and Scheduled Function Calls

Functions and agent methods can be registered to be called periodically or scheduled to be run at a particular time. Decorators or explicit access to an agent’s core.periodic() method can be used for this purpose. The latter is especially useful if, for example, a decision needs to be made in an agent’s onstart method as to whether a periodic call should be initialized. @Core.periodic(t) def function(self,...): function_body or # inside some agent method self.core.periodic(t, function)

6.2. Agent Development 107 VOLTTRON Documentation, Release 3.5RC1

Subsystem

These features are available to all Agent subclasses. No extra imports are required.

Remote Procedure Calls

Remote Procedure Calls, or RPCs are a powerful way to interact with other agents. To make a function available to call by a remote agent just add the export decorator: @RPC.export def function(self,...): function_body

function can now be called by a remote agent agent with # vip identity is the identity (a string) of the agent # where function() is defined agent.vip.rpc.call(vip,'function').get(timeout=t)

Pubsub

Agents can publish and subscribe to topics. Like RPC, pubsub functions can be invoked via decorators or inline through vip. The following function is called whenever the agent sees a message starting with topic_prefix. @PubSub.subscribe('pubsub', topic_prefix) def function(self, peer, sender, bus, topic, headers, message): function_body

An agent can publish to a topic topic with the self.vip.pubsub.publish method.

Heartbeat

The heartbeat subsystem provides access to a periodic publish so that others can observe the agent’s status. Other agents can subscibe to the heartbeat topic to see who is actively publishing to it. It it turned off by default.

Health

The health subsystem adds extra status information to the an agent’s heartbeat. Setting the status will start the heartbeat if it wasn’t already.

Agent Skeleton

import logging

from volttron.platform.vip.agent import Agent, Core, PubSub, RPC from volttron.platform.agent import utils utils.setup_logging() _log= logging.getLogger(__name__)

108 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

class MyAgent(Agent): def __init__(self, config_path, **kwargs): self.config= utils.load_config(config_path)

@Core.receiver('onsetup') def onsetup(self, sender, **kwargs): pass

@Core.receiver('onstart') def onstart(self, sender, **kwargs): self.vip.heartbeat.start()

@Core.receiver('onstop') def onstop(self, sender, **kwargs): pass

@Core.receiver('onfinish') def onfinish(self, sender, **kwargs): pass

@PubSub.subscribe('pubsub','some/topic') def on_match(self, peer, sender, bus, topic, headers, message): pass

@RPC.export def my_method(self): pass def main(): utils.vip_main(MyAgent) if __name__ =='__main__': try: main() except KeyboardInterrupt: pass

6.2.3 Driver Development

Introduction

All Voltton drivers are implemented through the Master Driver Agent and are technically sub-agents running in the same process as the Master Driver Agent. Each of these driver sub-agents is responsible for creating an interface to a single device. Creating that interface is facilitated by an instance of an interface class. Currently there are two interface classes included: Modbus and BACnet.

Existing Drivers

In the directory for the Master Driver Agent you’ll see a directory called interfaces: -- master_driver | -- agent.py | -- driver.py | -- __init__.py | -- interfaces

6.2. Agent Development 109 VOLTTRON Documentation, Release 3.5RC1

| | -- __init__.py | | -- bacnet.py | | -- modbus.py | -- socket_lock.py -- master-driver.agent -- setup.py

The files bacnet.py and modbus.py implement the interface class for each respective protocol. (The BACnet interface is mostly just a pass-though to the BACnet Proxy Agent, but the Modbus interface is self contained.) Looking at those two files is a good introduction into how they work. The file name is used when configuring a driver to determine which interface to use. The name of the interface class in the file must be called Interface.

Interface Basics

A complete interface consists of two parts: One or more register classes and the interface class.

Register Class

The Base Interface class uses a Register class to describe the registers of a device to the driver sub-agent. This class is commonly sub-classed to store protocol specific information for the interface class to use. For example, the BACnet interface uses a sub-classed base register to store the instance number, object type, and property name of the point on the device represented by the register class. The Modbus interface uses several different Register classes to deal with the different types of registers on Modbus devices and their different needs. The register class contains the following attributes: • read_only - True or False • register_type - “bit” or “byte”, used by the driver sub-agent to help deduce some meta data about the point. • point_name - Name of the point on the device. Used by the base interface for reference. • units - units of the value, meta data for the driver • description - meta data for the driver • python_type - python type of the point, used to produce meta data. This must be set explicitly otherwise it default to int. Here is an example of a Registry Class for the BACnet driver: class Register(BaseRegister): def __init__(self, instance_number, object_type, property_name, read_only, pointName, units, description=''): super(Register, self).__init__("byte", read_only, pointName, units, description='') self.instance_number= int(instance_number) self.object_type= object_type self.property= property_name

Note that this implementation is incomplete. It does not properly set the register_type or python_type.

Interface Class

The Interface Class is what is instantiated by the driver sub-agent to do it’s work.

110 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1 configure(self, config_dict, registry_config_str) This method must be implemented by an Interface implementa- tion. • config_dict is a dictionary of key values pairs from the configuration file’s “driver_config” section. • registry_config_str is the contents of the “registry_config” entry in the driver configuration file. It is up to the Interface class to parse this file according to the needs of the driver. Here is an example taken from the BACnet driver: def configure(self, config_dict, registry_config_str): self.parse_config(registry_config_str) #Parse the configuration string. self.target_address= config_dict["device_address"] self.proxy_address= config_dict.get("proxy_address","platform.bacnet_proxy") self.ping_target(self.target_address) #Establish routing to the device if needed.

And here is the parse_config method (See BACnet Registry Configuration: def parse_config(self, config_string): if config_string is None: return

f= StringIO(config_string) #Python's CSV file parser wants a file like object.

configDict= DictReader(f) #Parse the CVS file contents.

for regDef in configDict: #Skip lines that have no address yet. if not regDef['Point Name']: continue

io_type= regDef['BACnet Object Type'] read_only= regDef['Writable'].lower() !='true' point_name= regDef['Volttron Point Name'] index= int(regDef['Index']) description= regDef['Notes'] units= regDef['Units'] property_name= regDef['Property']

register= Register(index, io_type, property_name, read_only, point_name, units, description= description)

self.insert_register(register)

Once a register is created it must be added with the insert_register method. get_point(self, point_name) This method must be implemented by an Interface implementation. Gets the value of a point from a device and returns it. Here is a simple example from the BACnet driver. In this case it only has to pass the work on to the BACnet Proxy Agent for handling. def get_point(self, point_name): register= self.get_register_by_name(point_name)

6.2. Agent Development 111 VOLTTRON Documentation, Release 3.5RC1

point_map= {point_name:[register.object_type, register.instance_number, register.property]} result= self.vip.rpc.call(self.proxy_address,'read_properties', self.target_address, point_map).get() return result[point_name]

Failure should be indicated by a useful exception being raised. (In this case the we just leave the Exception raised by the BACnet proxy un-handled. This could be improved with better handling when register that does not exist is requested.) The Register instance for the point can be retrieved with self.get_register_by_name(point_name) set_point(self, point_name, value) This method must be implemented by an Interface implementation. Sets the value of a point on a device and ideally returns the actual value set if different. Here is a simple example from the BACnet driver. In this case it only has to pass the work on to the BACnet Proxy Agent for handling. def set_point(self, point_name, value): register= self.get_register_by_name(point_name) if register.read_only: raise IOError("Trying to write to a point configured read only:"+point_name) args=[self.target_address, value, register.object_type, register.instance_number, register.property] result= self.vip.rpc.call(self.proxy_address,'write_property', *args).get() return result

Failure to raise a useful exception being raised. (In this case the we just leave the Exception raised by the BACnet proxy un-handled unless the point is read only.) scrape_all(self) This method must be implemented by an Interface implementation. This must return a dictionary mapping point names to values for ALL registers. Here is a simple example from the BACnet driver. In this case it only has to pass the work on to the BACnet Proxy Agent for handling. def scrape_all(self): point_map={} read_registers= self.get_registers_by_type("byte", True) write_registers= self.get_registers_by_type("byte", False) for register in read_registers+ write_registers: point_map[register.point_name]= [register.object_type, register.instance_number, register.property]

result= self.vip.rpc.call(self.proxy_address,'read_properties', self.target_address, point_map).get() return result self.get_registers_by_type allows you to get lists of registers by their type and if they are read only. (As BACnet currently only uses “byte”, “bit” is ignored.) As the procedure for handling all the different types in BACnet is the same we can bundle them all up into a single request from the proxy. In the Modbus protocol the distinction is important and so each category must be handled differently.

112 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

6.2.4 Developing Historian Agents

VOLTTRON provides a convenient base class for developing new historian agents. The base class automatically subscribes to all pertinent topics, cache published data to disk until it is successfully recorded to a historian, create the public facing interface for querying results, and spells out a simple interface for concrete implementation to meet to make a working Historian Agent. The VOLTTRON provides support for several historians without modification. Please use one of these if it fits your project criteria, otherwise continue reading. The base class also breaks data to publish into reasonably sized chunks before handing it off to the concrete imple- mentation for publication. The size of the chunk is configurable. The base class sets up a separate thread for publication. This way if publication code needs to block for a long period of time (up to 10s of seconds) this will no disrupt the collection of data from the bus or the functioning of the agent itself.

BaseHistorian

All Historians must inherit from the BaseHistorian class in volttron.platform.agent.base_historian and implement the following methods: publish_to_historian(self, to_publish_list)

This method is called by the BaseHistorian class when it has received data from the message bus to be published. to_publish_list is a list of records to publish in the form [ { '_id': 1, 'timestamp': timstamp, 'source': 'scrape', 'topic': 'campus/building/unit/point', 'value': 90, 'meta': {'units':'F'} } { ... } ]

• _id - ID of record. All IDs in the list are unique. This is used for internal record tracking. • timestamp - Python datetime object of the time data was published at timezone UTC • source - Source of the data. Can be scrape, analysis, log, or actuator. • topic - Topic data was published on. Prefix’s such as “device” are dropped. • value - Value of the data. Can be any type. • meta - Metadata for the value. Some sources will omit this entirely. For each item in the list the concrete implementation should attempt to publish (or discard if non-publishable) every item in the list. Publication should be batched if possible. For every successfully published record and every record that is to be discarded because it is non-publishable the agent must call report_handled on those records. Records that should be published but were not for whatever reason require no action. Future calls to publish_to_historian will include these unpublished records. publish_to_historian is always called with the oldest unhandled records. This allows the historian to no lose data due to lost connections or other problems. As a convenience report_all_handled can be called if all of the items in published_list were successfully handled.

6.2. Agent Development 113 VOLTTRON Documentation, Release 3.5RC1

query_topic_list(self)

Must return a list of all unique topics published. query_historian(self, topic, start=None, end=None, skip=0, count=None, order=None)

This function must return the results of a query in the form: {"values": [(timestamp1: value1), (timestamp2: value2), ...], "metadata": {"key1": value1, "key2": value2, ...}} metadata is not required (The caller will normalize this to {} for you if you leave it out) • topic - the topic the user is querying for. • start - datetime of the start of the query. None for the beginning of time. • end - datetime of the end of of the query. None for the end of time. • skip - skip this number of results (for pagination) • count - return at maximum this number of results (for pagination) • order - “FIRST_TO_LAST” for ascending time stamps, “LAST_TO_FIRST” for descending time stamps. historian_setup(self)

Implementing this is optional. This function is run on the same thread as the rest of the concrete implementation at startup. It is meant for connection setup.

6.2.5 Agent Development in Eclipse

The Eclipse IDE (integrated development environment), while not required for agent development, can be a powerful developmental tool. Download the IDE from the following links. Choose a download mirror closest to your location. For 32-bit machines For 64-bit machines To go to the main Eclipse webpage, go to http://eclipse.org/

Installing Eclipse

To install Eclipse, enter the following commands in a terminal: 1. Install Eclipse dependency: # apt-get install openjdk-7-jdk

2. After downloading the eclipse archive file, move the package to the opt directory (enter this command from a terminal in the directory where eclipse was downloaded): $ -xvf eclipse-java-mars-R-linux-gtk-x86_64.tar.gz # mv eclipse /opt/

• For 32-bit machines, replace “gtk-x86_64” with “linux-gtk” in the previous command. 3. Create desktop shortcut:

114 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

# touch /usr/share/applications/eclipse.desktop # nano /usr/share/applications/eclipse.desktop

Enter the following text, as shown in Figure 1, and save the file. To avoid typos, copy and paste the following: [Desktop Entry] Name=Eclipse Type=Application Exec=/opt/eclipse/eclipse Terminal=false Icon=/opt/eclipse/icon.xpm Comment=Integrated Development Environment NoDisplay=false Categories=Development;IDE Name[en]=eclipse

Figure 1. Eclipse Desktop File 4. Copy the shortcut to the desktop: $ cp /usr/share/applications/eclipse.desktop ~/Desktop/

Eclipse is now installed and ready to use.

Installing Pydev and EGit Eclipse Plug-ins

The transactional network code is stored in a Git repository. A plug-in is available for Eclipse that makes development more convenient (note: you must have Git installed on the system and have built the project). 1. Select Help. Select Install New Software (Figure 2).

6.2. Agent Development 115 VOLTTRON Documentation, Release 3.5RC1

Figure 2. Installing Eclipse EGit Plugin 2. Click the Add button (Figure 3).

116 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 3. Installing Eclipse EGit Plugin (continued) 3. As shown in Figure 4, enter the following information: • For name use: EGit • For location: http://download.eclipse.org/egit/updates

6.2. Agent Development 117 VOLTTRON Documentation, Release 3.5RC1

Figure 4. Installing Eclipse Egit Plugin (continued) 4. After clicking OK, check the Select All button. 5. Click through Next > Agree to Terms > Finish. Allow Eclipse to restart. 6. After installing Eclipse, you must add the PyDev plug-in to the environment. In Eclipse: • Select Help and select Install New Software. • Click the Add button. • As shown in Figure 5, enter the following information: – For name use: PyDev

118 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

– For location: http://pydev.org/updates – Click OK.

Figure 5. Installing Eclipse PyDev Plugin 7. Check the box for PyDev. 8. Click through Next > Agree to Terms > Finish. Allow Eclipse to restart.

Checkout VOLTTRON Project

VOLTTRON can be imported into Eclipse from an existing VOLTTRON project (VOLTTRON was previously checked out from GitHub) or a new download from GitHub.

6.2. Agent Development 119 VOLTTRON Documentation, Release 3.5RC1

Import VOLTTRON into Eclipse from an Existing Local Repository (Previously Downloaded VOLTTRON Project)

To import an existing VOLTTRON project into Eclipse, complete the following steps: 1. Select File and select Import (Figure 6).

Figure 6. Checking VOLTTRON with Eclipse from Local Source 2. Select Git. Select Projects from Git. Click the Next button (Figure 7).

120 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 7. Checking VOLTTRON with Eclipse from Local Source (continued) 3. Select Existing local repository and click the Next button (Figure 8).

6.2. Agent Development 121 VOLTTRON Documentation, Release 3.5RC1

Figure 8. Checking VOLTTRON with Eclipse from Local Source (continued) 4. Select Add (Figure 9).

122 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 9. Checking VOLTTRON with Eclipse from Local Source (continued) 5. Select Browse. Navigate to the top-level base VOLTTRON directory. Select OK (Figure 10).

6.2. Agent Development 123 VOLTTRON Documentation, Release 3.5RC1

Figure 10. Checking Out VOLTTRON with Eclipse from Local Source (continued) 6. Click Finish (Figure 11).

124 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 11. Checking Out VOLTTRON with Eclipse from Local Source (continued) 7. Click Next (Figure 12).

6.2. Agent Development 125 VOLTTRON Documentation, Release 3.5RC1

Figure 12. Checking Out VOLTTRON with Eclipse from Local Source (continued) 8. Select Import as general project. Click Next. Click Finish (Figure 13). The project will be imported into the workspace.

126 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 13. Checking Out VOLTTRON with Eclipse from Local Source (continued)

Import New VOLTTRON Project from GitHub

To import a new VOLTTRON project directly from GitHub into Eclipse, complete the following steps: 1. Select File and select Import (Figure 14).

6.2. Agent Development 127 VOLTTRON Documentation, Release 3.5RC1

Figure 14. Checking Out VOLTTRON with Eclipse from GitHub 2. Select Git. Select Projects from Git. Click the Next button (Figure 15).

128 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 15. Checking Out VOLTTRON with Eclipse from GitHub (continued) 3. Select Clone URI and select Next (Figure 16).

6.2. Agent Development 129 VOLTTRON Documentation, Release 3.5RC1

Figure 16. Checking Out VOLTTRON with Eclipse GitHub (continued) 4. Fill in https://github.com/VOLTTRON/volttron.git for the URI. If you have a GitHub account, enter a username and password in the User and Password sections. This is not required but will allow you to receive notifications from GitHub for VOLTTRON related news. (Figure 17)

130 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 17. Checking Out VOLTTRON with Eclipse from GitHub (continued) 5. Select the 3.x branch (Figure 18).

6.2. Agent Development 131 VOLTTRON Documentation, Release 3.5RC1

Figure 18. Checking Out VOLTTRON with Eclipse from GitHub (continued) 6. Select a location to save the local repository (Figure 19).

132 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 19. Checking Out VOLTTRON with Eclipse from GitHub (continued) 7. Select Import as general project. Select Next. Select Finish (Figure 20). The project will now be imported into the workspace.

6.2. Agent Development 133 VOLTTRON Documentation, Release 3.5RC1

Figure 20. Checking Out VOLTTRON with Eclipse from GitHub (continued) If the VOLTTRON project has not been built (/bootstrap.py file has not been run), proceed to ##Section 2.4 Building the VOLTTRON Platform## and follow the instruction for running the bootstrap.py script before proceeding to the following sections.

Linking Eclipses

PyDev must now be configured to use the Python interpreter packaged with VOLTTRON. 1. Select Window and select Preferences.

134 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

2. Expand the PyDev tree. 3. Select Interpreters and select Python interpreter. 4. Select New (Figure 21).

Figure 21. Configuring PyDev 5. Select Browse and navigate to the pydev-python file located at (/scripts/pydev-python) (Figure 22). 6. Select OK (Figure 22).

6.2. Agent Development 135 VOLTTRON Documentation, Release 3.5RC1

Figure 22. Configuring PyDev (continued) 7. Select All and uncheck the VOLTTRON base directory (Figure 23).

136 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 23. Configuring PyDev (continued) 8. In the Project/PackageExplorer view on the left, right-click on the project, PyDev, and set as PyDev Project (Figure 24).

6.2. Agent Development 137 VOLTTRON Documentation, Release 3.5RC1

Figure 24. Setting as PyDev Project 9. Switch to the PyDev perspective: Select Window. Select Perspective. Select Open Perspective. Select Other. Select PyDev (Figure 25). Eclipse should now be configured to use the project’s environment.

138 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 25. Setting PyDev Perspective in Eclipse

Running the VOLTTRON Platform and Agents

VOLTTRON and agents within VOLTTRON can now be run within Eclipse. This section will describe the process to run VOLTTRON and an agent within Eclipse.

Setup a Run Configuration for the Platform

The following steps describe the process for running VOLTTRON within Eclipse:

6.2. Agent Development 139 VOLTTRON Documentation, Release 3.5RC1

1. Select Run and select Run Configurations (Figure 26).

Figure 26. Running VOLTTRON Platform, Setting Up a Run Configuration 2. Select Python Run from the menu on left. Click the New launch configuration button (Figure 27).

140 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 27. Running VOLTTRON Platform, Setting Up a Run Configuration (continued) 3. Change the name (any name may be used but for this example the name VOLTTRON was chosen) and select the main module (/volttron/platform/main.py). 4. Select the Arguments tab and enter ‘-vv –developer-mode’ in the Program arguments field (Figure 28) then select the Run button.

6.2. Agent Development 141 VOLTTRON Documentation, Release 3.5RC1

Figure 28. Running VOLTTRON Platform, Setting Up a Run Configuration (continued) 5. If the run is successful, the console should appear similar to Figure 29. If the run does not succeed (red text describing why the run failed will populate the console), click the all stop icon (two red boxes overlaid) on the console and then retry.

Figure 29. Running VOLTTRON Platform, Console View on Successful Run

142 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Configure a Run Configuration for the Listener Agent

The following steps describe the process for configuring an agent within Eclipse: 1. Select Run and select Run Configurations (Figure 30).

Figure 30. Running the Listener Agent, Setting Up a Run Configuration 2. Select Python Run from the menu on left and click the New launch configuration button (Figure 31).

6.2. Agent Development 143 VOLTTRON Documentation, Release 3.5RC1

Figure 31. Running the Listener Agent, Setting Up a Run Configuration (continued) 3. Change the name (for this example Listener is used) and select the main module (/examples/ListenerAgent/listener/agent.py) (Figure 32).

144 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 32. Running the Listener Agent, Setting Up a Run Configuration (continued) 4. Click the Arguments tab and change Working directory to Default (Figure 33).

6.2. Agent Development 145 VOLTTRON Documentation, Release 3.5RC1

Figure 33. Running the Listener Agent, Setting Up a Run Configuration (continued) 5. In the Environment tab, select New and add the following environment variables (bulleted list below), as shown in Figure 34: • AGENT_CONFIG = /home//examples /ListenerAgent/config AGENT_CONFIG is the absolute path the agent’s configuration file. To access a remote message bus, use the VIP address as described in ##Section 3.5 Platform Management:VOLTTRON Management Central.##

146 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 34. Running the Listener Agent, Setting Up a Run Configuration 6. Click Run. This launches the agent. You should see the agent start to publish and receive its own heartbeat message (Figure 35).

Figure 35. Listener Agent Output on Eclipse Console The process for running other agents in Eclipse is identical to that of the Listener agent. Several useful development tools are available within Eclipse and PyDev that make development, debugging, and testing of agents much simpler.

6.2. Agent Development 147 VOLTTRON Documentation, Release 3.5RC1

Agent Creation Walkthrough

Developers should look at the Listener agent before developing their own agent. The Listener agent illustrates the basic functionality of an agent. The following example demonstrates the steps for creating an agent.

Agent Folder Setup

Create a folder within the workspace to help consolidate the code your agent will utilize. 1. In the VOLTTRON base directory, create a new folder TestAgent. 2. In TestAgent, create a new folder tester. This is the package where the Python code will be created (Figure 36).

148 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 36. Creating an Agent Test Folder

Create Agent Code

The following steps describe the necessary agent files and modules. 1. In tester, create a file called __init__.py, which tells Python to treat this folder as a package. 2. In the tester package folder, create the file testagent.py 3. Create a class called TestAgent. 4. Import the packages and classes needed:

6.2. Agent Development 149 VOLTTRON Documentation, Release 3.5RC1

from __future__ import absolute_import

from datetime import datetime import logging import sys

from volttron.platform.vip.agent import Agent, Core from volttron.platform.agent import utils

5. Set up a logger. The utils module from volttron.platform.agent builds on Python’s already robust logging module and is easy to use. Add the following lines after the import statements: utils.setup_logging() _log= logging.getLogger(__name__)

This agent will inherit features from the Agent class (base class) extending the agent’s default functionality. The class definition for the TestAgent will be configured as shown below (with __init__). class TestAgent(Agent): def __init__(self, config_path, **kwargs): super(TestAgent, self).__init__(**kwargs)

Setting up a Subscription

1. Create a startup method. This method is tagged with the decorator @Core.receiver("onstart"). The startup method will run after the agent is initialized. The TestAgent’s startup method will contain a subscription to the Listener agent’s heartbeat (heartbeat/listeneragent). The TestAgent will detect when a message with this topic is published on the message bus and will run the method specified with the callback keyword argument passed to self.vip.pubsub.subscribe. @Core.receiver("onstart") def starting(self, sender, **kwargs): ''' Subscribes to the platform message bus on the heatbeat/listeneragent topic ''' print('TestAgent example agent start-up function') self.vip.pubsub.subscribe('pubsub','heartbeat/listeneragent', callback=self.on_heartbeat)

2. Create the callback method. Typically, the callback is the response to a message (or event). In this simple example, the TestAgent will do a print statement and publish a message to the bus: def on_heartbeat(self, peer, sender, bus, topic, headers, message): '''TestAgent callback method''' print('Matched topic: {}, for bus: {}'.format(topic, bus)) self.vip.pubsub.publish('pubsub', 'testagent/publish', headers=headers, message='test publishing').get(timeout=30)

Argument Parsing Main Method

The test agent will need to be able to parse arguments being passed on the command line by the agent launcher. Use the utils.default_main method to handle argument parsing and other default behavior.

150 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

1. Create a main method that can be called by the launcher: def main(argv=sys.argv): '''Main method called by the eggsecutable.''' try: utils.vip_main(TestAgent) except Exception as e: _log.exception(e)

if __name__ =='__main__': # Entry point for script sys.exit(main())

Create Support Files for Test Agent

VOLTTRON agents need configuration files for packaging, configuration, and launching. The “setup.py” file details the naming and Python package information. The launch configuration file is a JSON-formatted text file used by the platform to launch instances of the agent.

Packaging Configuration

In the TestAgent folder, create a file called “setup.py”. This file sets up the name, version, required packages, method to execute, etc. for the agent. The packaging process will also use this information to name the resulting file. from setuptools import setup, find_packages

#get environ for agent name/identifier packages= find_packages('.') package= packages[0]

setup( name= package+'agent', version="0.1", install_requires=['volttron'], packages= packages, entry_points={ 'setuptools.installation':[ 'eggsecutable ='+ package+'.testagent:main', ] } )

Launch Configuration

In TestAgent, create a file called “testagent.launch.json”. This is the file the platform will use to launch the agent. It can also contain configuration parameters for the agent: { "agentid":"Test1" }

6.2. Agent Development 151 VOLTTRON Documentation, Release 3.5RC1

Testing the Agent

From a terminal, in the base VOLTTRON directory, enter the following commands (with the platform activated and VOLTTRON running): 1. Run pack_install script on TestAgent: $ ./scripts/core/pack_install.sh TestAgent TestAgent/config test-agent

• Upon successful completion of this command, the terminal output will show the install directory, the agent UUID (unique identifier for an agent; the UUID shown in red is only an example and each instance of an agent will have a different UUID) and the agent name (blue text): Installed /home/volttron-user/.volttron/packaged/testeragent-0.1-py2-none-any.whl as d4ca557a-496c-4f02-8ad9-42f5d435868a testeragent-0.1

2. Start the agent: $ volttron-ctl start --tag test-agent

3. Verify that the agent is running: $ volttron-ctl status $ tail -f volttron.log

If changes are made to the Passive AFDD agent’s configuration file after the agent is launched, stop and reload the agent. In a terminal, enter the following commands: $ volttron-ctl stop --tag test-agent $ volttron-ctl remove --tag test-agent

Re-build and start the updated agent (Figure 37).

Figure 37. TestAgent Output In VOLTTRON Log

Running the TestAgent in Eclipse

If you are working in Eclipse, create a run configuration for the TestAgent based on the Listener agent configuration in the Eclipse development environment ##(Section 5.5.5 Running the VOLTTRON Platform and Agents)##. 1. Launch the platform (##Section 5.5.5.1 Setup a Run Configuration for the Platform##) 2. Launch the TestAgent by following the steps outlined in ##Section 5.5.5.2 *Configure a Run Configuration for the Listener Agent*## for launching the Listener agent. 3. Launch the Listener agent. TestAgent should start receiving the heartbeats from Listener agent and the following should be displayed in the console (Figure 38).

152 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Figure 38. Console Output for TestAgent

Adding Additional Features to the TestAgent

Additional code can be added to the TestAgent to utilize additional services in the platform. The following sections show how to use the weather and device scheduling service within the TestAgent.

Subscribing to Weather Data

This agent can be modified to listen to weather data from the Weather agent by adding the following line at the end of the TestAgent startup method. This will subscribe the agent to the temperature subtopic. For the full list of topics available, please see: https://github.com/VOLTTRON/volttron/wiki/WeatherAgentTopics self.vip.pubsub.subscribe('pubsub','weather/temperature/temp_f', callback=self.on_weather)

Add the callback method on_weather: def on_weather(self, peer, sender, bus, topic, headers, message): print("TestAgent got weather\nTopic: {}, Message: {}".format(topic, message))

The platform log file should appear similar to Figure 39.

Figure 39. TestAgent Output when Subscribing to Weather Topic

Utilizing the Scheduler Agent

The TestAgent can be modified to publish a schedule to the Actuator agent by reserving time on virtual devices. Modify the following code to include current time ranges and include a call to the publish schedule method in setup. The following example posts a simple schedule. For more detailed information on device scheduling, please see: https://github.com/VOLTTRON/volttron/wiki/ActuatorAgent Ensure the Actuator agent is running as per ##Section 3.3 Device Control: Configuring and Launching the Actuator Agent##. Add the following line to the TestAgent’s import statements: from volttron.platform.messaging import topics

Add the following lines to the TestAgent’s starting method. This sets up a subscription to the ACTUA- TOR_RESPONSE topic and calls the publish_schedule method. self.vip.pubsub.subscribe('pubsub', topics.ACTUATOR_RESPONSE, callback=self.on_schedule_result) self.publish_schedule()

The publish_schedule method sends a schedule request message to the Actuator agent (Update the schedule with appropriate times): def publish_schedule(self): headers={ 'AgentID': self._agent_id, 'type':'NEW_SCHEDULE',

6.2. Agent Development 153 VOLTTRON Documentation, Release 3.5RC1

'requesterID': self._agent_id, # Name of requesting agent 'taskID': self._agent_id+"-TASK", # Unique task ID 'priority':'LOW' # Task Priority (HIGH, LOW, LOW_PREEMPT) } msg=[ ["campus/building/device1", # First time slot. "2014-1-31 12:27:00", # Start of time slot. "2014-1-31 12:29:00"], # End of time slot. ["campus/building/device1", # Second time slot. "2014-1-31 12:26:00", # Start of time slot. "2014-1-31 12:30:00"], # End of time slot. ["campus/building/device2", # Third time slot. "2014-1-31 12:30:00", # Start of time slot. "2014-1-31 12:32:00"], # End of time slot. #etc... ] self.vip.rpc.call('platform.actuator', # Target agent 'request_new_schedule', # Method to call agent_id, # Requestor "some task", # TaskID "LOW", # Priority msg).get(timeout=10) # Request message

Add the call back method for the schedule request: def on_schedule_result(self, topic, headers, message, match): print (("TestAgent schedule result \nTopic: {topic}," "{headers}, Message: {message}") .format(topic=topic, headers=headers, message=message))

Full TestAgent Code

The following is the full TestAgent code built in the previous steps: from __future__ import absolute_import from datetime import datetime import logging import sys from volttron.platform.vip.agent import Agent, Core from volttron.platform.agent import utils from volttron.platform.messaging import headers as headers_mod utils.setup_logging() _log= logging.getLogger(__name__) class TestAgent(Agent): def __init__(self, config_path, **kwargs): super(TestAgent, self).__init__(**kwargs)

@Core.receiver("onstart") def starting(self, sender, **kwargs): ''' Subscribes to the platform message bus on the heatbeat/listeneragent topic '''

154 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

_log.info('TestAgent example agent start-up function') self.vip.pubsub.subscribe(peer='pubsub', topic='heartbeat/listeneragent', callback=self.on_heartbeat) self.vip.pubsub.subscribe('pubsub', topics.ACTUATOR_RESPONSE, callback=self.on_schedule_result) self.vip.pubsub.subscribe('pubsub','weather/temperature/temp_f', callback=self.on_weather)

self.publish_schedule()

def on_heartbeat(self, peer, sender, bus, topic, headers, message): '''TestAgent callback method''' _log.info('Matched topic: {}, for bus: {}'.format(topic, bus)) self.vip.pubsub.publish(peer='pubsub', topic='testagent/publish', headers=headers, message='test publishing').get(timeout=30)

def on_weather(self, peer, sender, bus, topic, headers, message): _log.info( "TestAgent got weather\nTopic: {}, Message: {}".format(topic, message))

def on_schedule_result(self, topic, headers, message, match): print (("TestAgent schedule result \nTopic: {topic}," "{headers}, Message: {message}") .format(topic=topic, headers=headers, message=message)) def main(argv=sys.argv): '''Main method called by the eggsecutable.''' try: utils.vip_main(TestAgent) except Exception as e: _log.info(e) if __name__ =='__main__': # Entry point for script sys.exit(main())

6.2.6 TestAgent Source Code

Full code of agent detailed in AgentDevelopment: import sys from volttron.platform.vip.agent import Agent, PubSub from volttron.platform.agent import utils class TestAgent(Agent):

def __init__(self, config_path, **kwargs): super(TestAgent, self).__init__(**kwargs)

@PubSub.subscribe('pubsub','heartbeat/listeneragent') def on_heartbeat_topic(self, peer, sender, bus, topic, headers, message): print "TestAgent got\nTopic: {topic}, {headers}, Message: {message}".format(topic=topic, headers=headers, message=message)

6.2. Agent Development 155 VOLTTRON Documentation, Release 3.5RC1

def main(argv=sys.argv): '''Main method called by the platform.''' utils.vip_main(TestAgent) if __name__ =='__main__': # Entry point for script try: sys.exit(main()) except KeyboardInterrupt: pass

Contents of setup.py for TestAgent: packages= find_packages('.') package= packages[0] setup( name= package+'agent', version="0.1", install_requires=['volttron'], packages= packages, entry_points={ 'setuptools.installation':[ 'eggsecutable ='+ package+'.agent:main', ] } )

Contents of testagent.config { "agentid":"Test1", "message":"hello" }

6.3 Deployment Advice

6.3.1 Platform Hardening for VOLTTRON

Rev. 0 | 1/29/2015 | Initial Document Development Rev. 1 | 2/5/2015 | Integrate comments from extended VOLTTRON team.

6.3.2 Introduction

VOLTTRON is an agent-based application development platform for distributed control systems. VOLTTRON itself is built with modern security principles in mind [security-wp] and implements many security features for hosted agents. However, VOLTTRON is built on top of Linux and the underlying Linux platform also needs to be secured in order to declare the resulting control system as “secure.” Any system is only as secure as its weakest link. The rest of this note is dedicated to

156 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1 making recommendations for hardening of the underlying Linux platform that VOLTTRON uses. Note that no system can be 100% secure and the cyber security strategy that is recommended in this document is based on risk management.

6.3.3 Linux System Hardening

Here are the non-exhaustive recommendations for Linux hardening from the VOLTTRON team:

• Physical Security: Keep the system in locked cabinets or a locked room. Limit physical access to systems and to the networks | to which they are attached. The goal should be to avoid physical access by | untrusted personnel. This could be extended to blocking or locking USB ports, | removable media drives, etc. | Drive encryption could be | used to avoid access via alternate-media booting (off USB stick or DVD) if | physical access can’t be guaranteed. Downside of drive encryption would be | needing to enter a passphrase to start system. Alternately, the | Trusted Platform Module (TPM) may be used, but the drive might still | be accessible to those with physical access. Enable chassis | intrusion detection and reporting if supported. If available, use a | physical tamper seal along with or in place of an interior switch. • Low level device Security: Keep firmware of all devices (including BIOS) up-to-date. Password-protect the BIOS. Disable unneeded/unnecessary devices including serial, parallel, USB, Firewire, etc. ports; optical drives; wireless devices, such as Wi-Fi and Bluetooth. Leaving a USB port enabled may be helpful if a | breach occurs to allow saving forensic data to an external drive. • Boot security: Disable automounting of external devices. Restrict the boot device. Disable PXE and other network boot options (unless | that is the primary boot method). Disable booting from USB and other | removable drives. Secure the boot loader. Require an administrator | password to do anything but start the default kernel. Do not allow | editing of kernel parameters. Disable, remove, or password-protect | emergency/recovery boot entries. • Security Updates: First and foremost, configure the system to automatically download security updates. Most security updates can be installed without rebooting the system, but some updated (e.g. shared libraries, kernel, etc) require the system to be rebooted. If possible, configure the system to install the security | updates automatically and reboot at a particular time. We also | recommend reserving the reboot time (e.g. 1:30AM on a Saturday | morning) using the Actuator Agent so that no control actions can | happen during that time. • System Access only via Secured Protocols: Disallow all clear text access to VOLTTRON systems. No telnet, no rsh, no ftp and no exceptions. Use ssh to gain console access, and scp/sftp to get files in | and out of the system. Disconnect excessively idle SSH Sessions. • Disable remote login for “root” users. Do not allow a user to

6.3. Deployment Advice 157 VOLTTRON Documentation, Release 3.5RC1

directly access the system as the “root” user from a remote network | location. Root access to privileged operations can be accomplished | using “sudo” This adds an extra level of security by restricting | access to privileged operations and tracking those operations | through the system log. • Manage users and usernames. Limit the number of user accounts. Use complex usernames rather than first names. • Authentication. If possible, use two factor authentication to allow access to the system. Informally, two factor authentication uses a combination of “something you know” and “something you have” to allow access to the system. RSA SecurID tokens are commonly used for two factor authentication but other tools are available. When not using two-factor authentication, use strong passwords and do not share accounts. • Scan for weak passwords. Use password cracking tools such as John the Ripper (http://www.openwall.com/john/) or nmap with password cracking modules (http://nmap.org) to look for weak passwords. • Utilize Pluggable Authentication Modules (PAM) to strengthen passwords and the login process. We recommend: – pam_abl: Automated blacklisting on repeated failed authentication attempts – pam_captcha: A visual text-based CAPTCHA challenge module for PAM – pam_passwdqc: A password strength checking module for PAM-aware password changing programs – pam_cracklib: PAM module to check the password against dictionary words – pam_pwhistory: PAM module to remember last passwords • Disable unwanted services. Most desktop and server Linux distributions come with many unnecessary services enabled. Disable all unnecessary services. Refer to your distribution’s documentation | to discover how to check and disable these services. • Just as scanning for weak passwords is a step to more secure systems, | regular network scans using Nmap (www.nmap.org) to find what network | services are being offered is another step towards a more secure | system. Note, use nmap or similar tools very carefully on BACnet and modbus | environments. These scanning tools are known to crash/reset BACnet and modbus | devices. • Control incoming and outgoing network traffic. Use the built-in host-based firewall to control who/what can connect to this system. Many iptables frontends offer a set of predefined rules that | provide a default deny policy for incoming connections and provide | rules to prevent or limit other well known attacks (i.e. rules that | limit certain responses that might amplify a DDoS attack). ufw | (uncomplicated firewall) is a good example. | For example, if the system administrators for the VOLTTRON | device are all located in 10.10.10.0/24 subnetwork, then allow SSH | and SCP logins from only that IP address range. If VOLTTRON system | exports data to a historian at 10.20.20.1 using TCP port 443, allow | outgoing traffic to that port on that server. The idea here is to | limit the attack surface of the system. The smaller the surface, the | better we can analyze the communication patterns of the system and | detect anomalies. One word of caution. While some system | administrators disable network-based diagnostic tools such as ICMP | ECHO responses, VOLTTRON team believes that this hampers | usability. As an example, monitoring which incoming

158 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

and outgoing | firewall rules are triggering can be accomplished with this command: | watch --interval=5 ’iptables -nvL | grep -v "0 0"’ . • Rate limit incoming connections to discourage brute force hacking attempts. Use a tool such as fail2ban (http://www.fail2ban.org/wiki/index.php/Main_Page) to dynamically manage firewall rules to rate limit incoming connections and discourage brute force hacking attempts. sshguard (http://www.sshguard.net/) is similar to fail2ban but only used for ssh connections. Further rate limiting can be accomplished at the firewall level. As an example, you can restrict the number of connections used by a single IP address to your server using iptables. Only allow 4 ssh connections per client | system: | iptables -A INPUT -p tcp --syn --dport 22 -m connlimit --connlimit-above 4 -j DROP | You can limit the number of connections per minute. The fol- lowing example will drop incoming connections if an IP address makes more than 10 connection attempts to port 22 within 60 seconds: | iptables -A INPUT -p tcp -dport 22 -i eth0 -m state --state NEW -m recent --set | iptables -A INPUT -p tcp -dport 22 -i eth0 -m state --state NEW -m recent \ --update --seconds 60 --hitcount 10 -j DROP • Use a file system integrity tool to monitor for unexpected file changes. Tools such as tripwire (http://sourceforge.net/projects/tripwire/) to monitor filesystem for changed files. Another file integrity checking tool to consider | is AIDE (Advanced Intrusion Detect Environment) | (http://aide.sourceforge.net/). • Use filesystem scanning tools periodically to check for exploits. Available tools such as checkrootkit (http://www.chkrootkit.org), rkhunter (http://rkhunter.sourceforge.net) and others should be used to check | for known exploits on a periodic basis and report their results. • VOLTTRON does not use apache or require it. If Apache is being used, | we recommend using mod_security and mod_evasive modules.

6.3.4 System Monitoring

• Monitor system state and resources. Use a monitoring tool such as Xymon (http://xymon.sourceforge.net) or (http://www.bb4.org/features.html) to remotely monitor the system resources and state. Set the monitoring tools to alert the system administrators if anomalous use of resources (e.g. connections, memory, etc) are detected. An administrator can also use unix commands such as netstat to look for open connections periodically. • Watch system logs and get logs off the system. Use a utility such as | logwatch (http://sourceforge.net/projects/logwatch/files/) or | logcheck (http://logcheck.org) to get | daily summary of system activity via email. For Linux distributions | that use systemd, use journalwatch |

6.3. Deployment Advice 159 VOLTTRON Documentation, Release 3.5RC1

(http://git.the-compiler.org/journalwatch/) | to accomplish the same task. | Additionally, use a remote syslog server to collect logs from all | VOLTTRON systems in | the field at a centralized location for analysis. A tool such as | splunk is ideal for this task and comes with many built-in analysis | applications. Another benefit of sending logs remotely off the platform | is the ability to inspect the logs even when the platform may be | compromised. • An active intrusion sensor such as PSAD (http://cipherdyne.org/psad/) can be used to look for intrusions as well.

6.3.5 Security Testing

Every security control discussed in the previous sections must be tested to determine correct operation and impact. For example, if we inserted a firewall rule to ban connections from an IP address such as 10.10.10.2, then we need to test that the connections actually fail.

In addition to functional correctness testing, common security testing tools such as Nessus (http://www.tenable.com/products/nessus) and nmap (http://nmap.org) should be used to perform cyber security testing.

6.3.6 Conclusion

No system is 100% secure unless it is disconnected from the network and is in a physically secure location. VOLT- TRON team recommends a risk-based cyber security approach that considers each risk, and the impact of an exploit. Mitigating technologies can then be used to mitigate the most impactful risks first. VOLTTRON is built with security in mind from the ground up. But it is only as secure as the that it runs on top of. This document is intended to help VOLTTRON users to secure the underlying Linux operating system to further improve the robustness of the VOLTTRON platform. Any security questions should be directed to [email protected].

6.3.7 Platform External Address Configuration

In the configuration file located in $VOLTTRON_HOME/config add vip-address=tcp://ip:port for each address you want to listen on Example vip-address=tcp://127.0.0.102:8182 vip-address=tcp://127.0.0.103:8083 vip-address=tcp://127.0.0.103:8183

6.3.8 Deployment Recommendations

Ensuring the safe operation of devices working with VOLTTRON is essential. This page will discuss recommenda- tions/experiences for doing deployments to meet these needs. In a previous deployment, VOLTTRON worked with a facilities manager to create virtual points which the platform would control:

160 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

• VolttronEnabled • VolttronHeartbeat • VoltronOverride • VolttronCoolingSetPoint When VOLTTRON is active it sets the VolttronEnabled flag. It also twiddles the VolttronHeartbeat point every minute. While the Enabled flag is set and the Heartbeat is active, the controller will use VOLTTRON specific points instead of base points (VolttronCoolingSetPoint instead of CoolingSetPoint). If VolttronOveride is set then the controller ignores the VOLTTRON points and reverts to normal operation. Likewise, if the Heartbeat point is not being actively set by the platform or if VolttronEnabled is false, then the controller reverts to normal control. If VolttronEnabled and (active Heartbeat) and not (VolttronOverride) then use Volttron virtual points

6.4 Walkthroughs

6.4.1 Deployment Walkthrough

This page is meant as an overview of setting up a VOLTTRON deployment which consists of one or more platforms collecting data and being managed by another platform running the VOLTTRON Central agent. High level instructions are included but for more details on each step, please follow links to that section of the wiki. Assumptions: • “Data Collector” is the box that has the drivers and is collecting data it needs to forward. • “Volttron Central/VC” is the box that has the historian which will save data to the database. • VOLTTRON_HOME is assumed to the default on both boxes which is: /home//.volttron Notes/Tips: • Aside from installing the required packages with apt-get, sudo is not required and should not be used. VOLT- TRON is designed to be run as a non-root user and running with sudo is not supported. • The convenience scripts have been developed to simplify many of the of the repetitive multi-step processes. For instance, scripts/core/make-listener can be modified for any agent and make it one command to stop, remove, build, install, configure, tag, start, and (optionally) enable an agent for autostart. • These instructions assume default directories are used (for instance, /home//volttron for the project directory and /home//.volttron for the VOLTTRON Home directory. • Creating a separate config directory for agent configuration files used in the deployment can prevent them from being committed back to the repository. • Double check firewall rules/policies when setting up a multi-node deployment to ensure that platforms can communicate

On all machines:

On all machines in the deployment, setup the platform, setup encryption, authentication, and authorization. Also, build the basic agents for the deployment. All platforms will need a PlatformAgent and a Historian. Using scripts will help simplify this project.

6.4. Walkthroughs 161 VOLTTRON Documentation, Release 3.5RC1

Install required packages

• sudo apt-get install build-essential python-dev openssl libssl-dev libevent-dev git

Build the project

• Clone the repository and build using python bootstrap.py

Configuring Platform

On VC: • Run volttron-cfg • Setup as VOLTTRON Central. • Set appropriate ip, port, etc for this machine • Pick to install a platform historian (defaults to sqlite) • Start up the platform and find the line with the server public key “cat volttron.log|grep “public key”: 2016-05-19 08:42:58,062 () volttron.platform.main INFO: public key:

On the data collector:

Setup drivers

For a simple case, follow instructions to install a Fake Driver‘ for testing purposes. For an actual deployment against real devices see the following: • Create a Master Driver Agent to coordinate drivers for the devices controlled by this platform. • For MODBUS devices, create config files and point configuration files. • For BACnet devices, create a Proxy Agent for BACnet drivers to communicate through Now that data is being published to the bus, a Forward Historian can be configured to send this data to the VC instance for storage. • Use: volttron-ctl keypair to generate a keypair • cat VOLTTRON_HOME/keypair to get the public and secret keys • Create a config directory in the main project directory • Setup a Forward Historian – cp services/core/ForwardHistorian/config config/forwarder.config – Edit forwarder.config using the VC’s VIP address, the public server key, and the keypair -“destination-vip”: “tcp://:?serverkey=&secretkey=&publickey= – For ease of use, you can create a script to install the historian: – cp scripts/core/make-listener ./make-forwarder, then edit to look like:

162 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

make-forwarder:: export SOURCE=services/core/ForwardHistorian export CONFIG=config/forwarder.config ex- port TAG=forward ./scripts/core/make-agent.sh enable • Execute that script and the forward historian should be installed To check that things are working: Start a listener agent on VC, you should see data from the data collector appear In the log for VC, check for credentials success for the ip of data collector.

Registering the collection platform

• In a browser, go to the url for your VC instance. • Click on Register Platforms • Enter a name for the collection platform and the ip configured http://: • Open the tree upper left of the UI and find your platform.

Troubleshooting:

• Check firewall rules registering VC on VC: ipc://@/home/volttron/.volttron/run/vip.socket Change password by putting pw hash in config file Add remote ip address to config file

6.4.2 Forward Historian Walkthrough

This guide describes a simple setup where one Volttron instance collects data from a dummy driver and sends it to another instance where it can be recorded. I’m doing this example on a single machine. If you’re doing the same I recommend the following: • Set your Volttrons’ vip addresses to use tcp. It’s not needed but is closer to what a real deployment would have. If you’re looking for open ports then $ netstat -ln --tcp will be helpful. • Be careful with your terminal windows’ environment variables. At least one of your Volttron instances won’t be able to live in ~/.volttron • If you run into trouble it can be helpful to run each agent in the foreground of its own terminal. This can be accomplished with the following: – Go to the agent’s source directory – Locate the agent’s configuration file and the python file with a main function (frequently called agent.py) – For the forwarding historian, the default config file is in the services/core/ForwardHistorian directory and its agent.py file is at services/core/ForwardHistorian/forwarder/agent.py – Use $ AGENT_CONFIG=config python -m forwarder.agent (no .py!) to run the forward- ing historian in the foreground.

Configuration

1. Set up two Volttron instances as described in Deployment Walkthrough 2. In each Volttron’s auth.json file add the public key and location of the other Volttron instance to the "allow" array.

6.4. Walkthroughs 163 VOLTTRON Documentation, Release 3.5RC1

3. Now the Volttron instances should be able to communicate. To avoid having to worry about authentication for the remainder of the walkthrough you can use --developer-mode when starting Volttron.

Forwarding Volttron

This Volttron will have a fake driver provided by the Master Driver and a Forwarding Historian. You may have to edit these configuration files before starting them: services/core/ForwardHistorian/config services/core/MasterDriverAgent/fake-msater-driver.agent services/core/MasterDriverAgent/master_driver/test_fakedriver.config

Remote Volttron

This Volttron will receive the history sent from the Volttron instance we’ve already set up. Having a Listener Agent running in the foreground (scripts/launch_listener.sh) will make it easy to verify that the data has been successfully received. VOLTTRON Central is a platform management web application that allows platforms to communicate and to be managed from a centralized server. This agent alleviates the need to ssh into independent nodes in order to manage them. The demo will start up 3 different instances of VOLTTRON with 3 historians and different agents on each host. The following entries will help to navigate around the VOLTTRON Central interface. • Running the Demo • Stopping the Demo • The Login • Logging Out • Platforms Screen • Register new Platform • Deregister Platform • Platform View • Start and Stop Agents • Add Chart • Edit Chart

6.4.3 Running the Demo

After building VOLTTRON, open a shell with the current directory the root of the volttron repository. Activate the shell . env/bin/activate execute the script ./volttron/scripts/management-service-demo/run-demo

164 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Upon completion a browser window (opened to http://localhost:8080/) should be opened with a login prompt and the shell should look like the following image.

1. Log into the front page using credentials admin/admin. 2. From the console window copy the first platform address from the shell. 3. In the upper right of the browser window, click Platforms, then click Register Platform. 4. Type ‘Platform 1’ in the name parameter and paste the first platforms ipc address that you copied from step 2. • The Platform 1 should show up in the list of platforms on this page. 1. Repeat step 4 for the other 2 platforms.

Stopping the Demo

Once you have completed your walk through of the different elements of the VOLTTRON Central demo you can stop the demos by executing ./scripts/management-service-demo/stop-platforms.sh

Once the demos is complete you may wish to see the VOLTTRON Central page for more details on how to configure the agent for your specific use case.

Login

To login to the Management Platform, navigate in a browser to localhost:8080, and enter the username and password on the login screen. |

6.4. Walkthroughs 165 VOLTTRON Documentation, Release 3.5RC1

Logout

To logout of the Management Platform, click the link at the top right

166 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

of the screen. |

6.4.4 Platforms Screen

This screen lists the registered VOLTTRON Platforms, and allows new platforms to be registered by clicking the button in the top right corner of the screen. This includes the Platform UID as well as the number of agents running, stopped and installed on each platform. |

6.4. Walkthroughs 167 VOLTTRON Documentation, Release 3.5RC1

Register new Platform

To register a new VOLTTRON Platform, click the Button in the corner of the screen. You will need to provide a name and the IP address of the VOLTTRON Platform. |

Deregister Platform

To deregister a VOLTTRON Platform, click on the ‘X’ at the far right of the platform display.

168 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

6.4.5 Platform View

Use the Platform View to manage a specific VOLTTRON Platform. This includes installing agents, start/stop agents, and configuring charts. |

Install Agent

To install a new agent, all you need is the agent’s wheel file. Click on the button To upload the wheel file used to install the agent.

Start and Stop Agents

To Start or Stop an Agent, click on the button as shown in the figure.

If the agent is running, its PID will be displayed. |

6.4. Walkthroughs 169 VOLTTRON Documentation, Release 3.5RC1

Add Chart

To add a chart, click the Add Chart button. You will need to provide the published topic the chart pulls data from. You may also select re- fresh interval and chart type as well as pin the chart to the dashboard. |

Edit Chart

To edit a chart, click the edit chart button. You will get a popup

170 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1 window of settings for the chart, as shown in the figure. To pin the chart to the dashboard select the checkbox. |

6.4.6 VOLTTRON Quick Start Guide

Note: the content of the user guide is being incorporated into the readthedocs site. Some quick links to get started are: Install required software Building the VOLTTRON project Agent Development VOLTTRON Development in Eclipse

6.4.7 Example Multi-platform deployment

These instructions are intended for early adopters who wish to setup VOLTTRON 3.0 platforms on multiple com- puters communicating with a VOLTTRON Central instance. VOLTTRON 3.0 is still under development and these instructions will change as the process is simplified. On the machine running the VOLTTRON Central agent

6.4. Walkthroughs 171 VOLTTRON Documentation, Release 3.5RC1

Modify a copy of volttron/scripts/management-service-demo/volttron-central-config | Change the ip address to the ip of the machine hosting VOLTTRON Central | “server” : { | “host”: “”, | “port”: 8080 | }, Replace the hashed passwords with your own using: import hashlib, uuid hashlib.sha512("your password").hexdigest()

Package, install, and start the volttron central agent using this config file. On all machines:

To run without encryption Truncate to 0 bytes, or delete then touch ~/.volttron/curve.key

Create a config file to set VIP socket to an externally visible port create the file: ~/.volttron/config with the content: vip-address=tcp://:8081

Create a configuration for the platform agent filename: platform-config

{ “agentid”: “Your Agent Name”, “agent_type”: “platform node”, “volttron_central_vip_address”: “tcp://:8081“ }

Package, install, and start the platform agent agent. Once all this is done, go to http://:8080/ Log in using the password you created for admin above.

Open the console tab to see the authorization string: “authorization”: “ed97f9e5-6f6e-49f4-92ca-d532eb66ab83”

In another browser go to: http://:8080/register-platforms.html

Put the auth string into “Auth Token”

172 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Set vip.identity to “platform.agent” Set Agent Id to any string you wish to use to identify this platform Set VIP Address to the vip address setup in the config file for the platform you are attempting to register After clicking “Register Platform” go back to the other browser and refresh it. The registered platform should appear.

6.5 Eclipse IDE Setup

6.5.1 Eclipse Development Environment

The only thing that is necessary to create a VOLTTRON agent is a text editor and the shell. However, we have found the Eclipse Development Environment (IDE) to be a valuable tool for helping to develop VOLTTRON agents. You can obtain the latest (MARS as fo 10/7/15) from http://www.eclipse.org/. Once downloaded the PyDev Plugin is a valuable tool for executing the platform as well as debugging agent code. • Install PyDev Plugin • Clone the VOLTTRON Source • Build VOLTTRON • Link Eclipse to VOLTTRON Python Environment • Make Project a PyDev Project • Testing the Installation • Execute VOLTTRON Through Shell • Execute VOLTTRON Through Eclipse • Start a ListenerAgent

PyDev Plugin

Installing the PyDev plugin from the Eclipse Market place There is a python plugin for eclipse that makes development much easier. Install it from the eclipse marketplace.

6.5. Eclipse IDE Setup 173 VOLTTRON Documentation, Release 3.5RC1

174 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

Cloning the Source Code

The VOLTTRON code is stored in a git repository. Eclipse (Luna and Mars) come with a git plugin out of the box. For other versions the plugin is available for Eclipse that makes development more convenient (note: you must have Git already installed on the system and have built VOLTTRON): If your version of Eclipse does not have the marketplace follow these [[instructions|Manual-Plugin-Install]]. The project can now be checked out from the repository into Eclipse. 1. Open the Git view

6.5. Eclipse IDE Setup 175 VOLTTRON Documentation, Release 3.5RC1

2. Clone a Git Repository

176 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

3. Fill out the URI: https://github.com/VOLTTRON/volttron

6.5. Eclipse IDE Setup 177 VOLTTRON Documentation, Release 3.5RC1

4. Select 3.x branch for latest release (**master for latest stable

178 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

version**) | 5. Import the cloned repository as a general project

6.5. Eclipse IDE Setup 179 VOLTTRON Documentation, Release 3.5RC1

6. Pick a project name (default volttron) and hit Finish

180 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

7. Switch to the PyDev perspective

Build VOLTTRON

Continue the setup process by opening a command shell. Make the current directory the root of your cloned VOLT- TRON directory. Follow the instructions in our Building VOLTTRON section of the wiki and then continue below.

Linking Eclipse and the VOLTTRON Python Environment

From the Eclipse IDE right click on the project name and select Refresh so eclipse will be aware of the file system changes. The next step will define the python version that PyDev will use for VOLTTRON 1. Choose Window - > Preferences 2. Expand the PyDev tree 3. Select Interpreters - > Python Interpreter 4. Click New 5. Click Browse and browse to the pydev-python file located in scripts directory off of the volttron source 6. Click Ok

6.5. Eclipse IDE Setup 181 VOLTTRON Documentation, Release 3.5RC1

7. Select All, then uncheck the VOLTTRON root like the picture below

182 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

8. Click Ok • You may need redo this stage after platform updates

Make Project a PyDev Project

1. In the Project/PackageExplorer view on the left, right-click on the project, PyDev-> Set as PyDev Project 2. Switch to the PyDev perspective (if it has not already switched), Window -> Open Perspective -> PyDev

6.5. Eclipse IDE Setup 183 VOLTTRON Documentation, Release 3.5RC1

Eclipse should now be configured to use the project’s environment.

Testing the Installation

In order to test the installation the VOLTTRON platform must be running. You can do this either through the shell or through Eclipse.

Execute VOLTTRON Platform from Shell

1. Open a console and cd into the root of the volttron repository. 2. Execute . env/bin/activate 3. Execute volttron -vv –developer-mode

184 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

You now have a running VOLTTRON logging to standard out. The next step to verifying the installation is to start a listeneragent.

Execute VOLTTRON Platform from Eclipse

1. Click Run -> Run Configuration from the Eclipse Main Menu 2. Click the New Launch Configuration button

3. Change the name and select the main module

6.5. Eclipse IDE Setup 185 VOLTTRON Documentation, Release 3.5RC1

volttron/platform/main.py | 4. Click the Arguments Tab add ‘-vv –developer-mode’ as in the following image.

186 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

• Change Working Directory to Default 1. Click Run. The following image displays the output of a successfully started platform

Start a ListenerAgent

The listener agent will listen to the message bus for any published messages. It will also publish a heartbeat message ever 10 seconds (by default). Create a new run configuration entry for the listener agent. 1. In the Package Explorer view, open examples -> ListenerAgent –> listener 2. Righ-click on agent.py and select Run As -> Python Run (this will create a run configuration but fail) 3. On the menu bar, pick Run -> Run Configurations... 4. Under Python Run pick “volttron agent.py” 5. Click on the Arguments tab • Change Working Directory to Default 1. In the Environment tab, click new set the variable to AGENT_CONFIG with the value of

6.5. Eclipse IDE Setup 187 VOLTTRON Documentation, Release 3.5RC1

/home/\/git/volttron/examples/ListenerAgent/config 2. Click Run, this launches the agent You should see the agent start to publish and receive its own heartbeat message in the console.

6.5.2 Manual Python Plugin Installation

If Eclipse doesn’t have the Marketplace, follow these steps to get the plugins: • Help -> Install New Software • Click on the “Add” button • For name use: EGit • For location: \ • After hitting OK, check the box for: Check Select All • Click through Next, Agree to Terms, then Finish • Allow Eclipse to restart After installing Eclipse, you must add the PyDev plugin to the environment. In Eclipse: • Help -> Install New Software • Click on the “Add” button • For name use: PyDev

188 Chapter 6. Developer Resources VOLTTRON Documentation, Release 3.5RC1

• For location: ‘\\\ ‘http://pydev.org/updates\\ ‘__ ‘__ • Check the box for PyDev • Click through Next, Agree to Terms, Finish • Allow Eclipse to restart

6.5. Eclipse IDE Setup 189 VOLTTRON Documentation, Release 3.5RC1

190 Chapter 6. Developer Resources CHAPTER 7

Example Agents and Uitlities

7.1 Example Agents

7.1.1 Example Agents Overview

Some example agents are included with the platform to help explore its features. • Data Publisher • Listener • Ping-Pong • Non-Python agents • Scheduling Example • Weather • Multi-Node More complex agents contributed by other researchers can also be found in the examples directory. It is recommended that developers new to VOLTTRON understand the example agents first before diving into the other agents.

Example Agent Conventions

Some of the example agent classes are defined inside a method, for instance: def ScheduleExampleAgent(config_path, **kwargs): config= utils.load_config(config_path) campus= config['campus']

This allows configuration information to be extracted from an agent config file for use in topics. @Pubsub.subscribe('pubsub', DEVICES_VALUE(campus=campus)) def actuate(self, peer, sender, bus, topic, headers, message):

7.1.2 Exampler Controller Agent

Note, this agent is out of date and will not work with the current actuator/scheduler. Please see [ActuatorAgent documentation] (ActuatorAgent “wikilink”).

191 VOLTTRON Documentation, Release 3.5RC1

This agent listens for outdoor temperature readings then changes the cool fan speed. It demonstrates pub/sub interac- tion with the RTU Controller. ‘Requirements ‘__ for running this agent (or any agent wishing to interact with the RTU: • – Edit the driver.ini file to reflect the sMAP key, uuid, and other settings for your installation – Activate the project Python from the project dir: . bin/activate – Launch the smap driver by starting (from the project directory): twistd -n smap your_driver.ini – Launch the ActuatorAgent just as you would launch any other agent With these requirements met, the • – Subscribe to the outside air temperature topic. – If the new reading is higher than the old reading then

* Request the actuator lock for the rtu – If it receives a lock request success it randomly sets the coolsupply fan to a new reading. – If it does not get the lock, it will try again the next time the temperature rises. – If the set result is a success, it releases the lock.

7.1.3 Fake Driver

The FakeDriver is included as a way to quickly see data published to the message bus in a format that mimics what a true Driver would produce. This is an extremely simple implementation of the VOLTTRON driver framework Make a script to build and deploy the fake driver. • Create a config directory (if one doesn’t already exist). All local config files will be worked on here. - cp examples/configurations/drivers/fake.config config/ - Edit registry_config for the paths on your system fake.config: { "driver_config": {}, "campus":"MyFakeCampus", "building":"SomeBuilding", "unit":"MyFakeDevice", "driver_type":"fakedriver", "registry_config":"/home//volttron/examples/configurations/drivers/fake.csv", "interval":5, "timezone":"US/Pacific", "heart_beat_point":"Heartbeat" }

• cp services/core/MasterDriverAgent/master-driver.agent config/fake-master-driver.config • Edit fake-master-driver.config to reflect paths on your system fake-master-driver.config: { "agentid": "master_driver", "driver_config_list": [ "/home//volttron/config/fake.config" ]

192 Chapter 7. Example Agents and Uitlities VOLTTRON Documentation, Release 3.5RC1

] }

• Create a script to simplify installation. The following will stop and remove any existing instances of agents create with the script, then package, install, and start the new instance. You will need to make the file executable: chmod +x make-fakedriver make-fakedriver: export SOURCE=services/core/MasterDriverAgent export CONFIG=config/fake-master-driver.agent export TAG=fake-driver ./scripts/core/make-agent.sh

• If you have a Listener Agent already installed, you should start seeing data being published to the bus.

7.1.4 ListenerAgent

The ListenerAgent subscribes to all topics and is useful for testing that agents being developed are publishing correctly. It also provides a template for building other agents as it expresses the requirements of a platform agent.

Explanation of ListenerAgent

ListenerAgent publishes a heartbeat message so it will use the PublishMixin. It also extends BaseAgent to get the default functionality. When creating agents, Mixins should be first in the class definition. from __future__ import absolute_import from datetime import datetime import logging import sys

from volttron.platform.vip.agent import Agent, Core, PubSub, compat from volttron.platform.agent import utils from volttron.platform.messaging import headers as headers_mod, topics from. import settings #If developing inside Eclipse, use pydev-launch.py # or change this to: import settings

Use utils to setup logging which we’ll use later. utils.setup_logging() _log = logging.getLogger(name)

The Listener agent extends (inherits from) the Agent class for its default functionality such as responding to platform commands: class ListenerAgent(Agent): '''Listens to everything and publishes a heartbeat according to the heartbeat period specified in the settings module. '''

7.1. Example Agents 193 VOLTTRON Documentation, Release 3.5RC1

After the class definition, the Listener agent reads the configuration file, extracts the configuration parameters, and initializes any Listener agent instance variable. This is done the agents init method: def __init__(self, config_path, **kwargs): super(ListenerAgent, self).__init__(**kwargs) self.config= utils.load_config(config_path)

Next, the Listener agent will run its setup method. This method is tagged to run after the agent is initialized by the decorator @Core.receiver(‘onsetup’). This method accesses the configuration parameters, logs a message to the platform log, and sets the agent ID. @Core.receiver('onsetup') def setup(self, sender, **kwargs): # Demonstrate accessing a value from the config file _log.info(self.config['message']) self._agent_id= self.config['agentid']

The Listener agent subscribes to all topics published on the message bus. Subscribe/publish interactions with the message bus are handled by the PubSub module located at: | ~/volttron/volttron/platform/vip/agent/subsystems/pubsub.py The Listener agent uses an empty string to subscribe to all messages published. This is done in a decorator for simplifying subscriptions. It also checks for the sender being pubsub.compat in case there are any VOLTTRON 2.0 agents running on the platform. @PubSub.subscribe('pubsub', '') def on_match(self, peer, sender, bus, topic, headers, message): '''Use match_all to receive all messages and print them out.''' if sender == 'pubsub.compat': message = compat.unpack_legacy_message(headers, message) _log.debug( "Peer: %r, Sender: %r:, Bus: %r, Topic: %r, Headers: %r, " "Message: %r", peer, sender, bus, topic, headers, message)

The Listener agent uses the @Core.periodic decorator to execute the publish_heartbeat method every HEART- BEAT_PERIOD seconds where HEARTBEAT_PERIOD is specified in the settings.py file: @Core.periodic(settings.HEARTBEAT_PERIOD) def publish_heartbeat(self): '''Send heartbeat message every HEARTBEAT_PERIOD seconds.

HEARTBEAT_PERIOD is set and can be adjusted in the settings module. ''' now= datetime.utcnow().isoformat('')+'Z' headers={ 'AgentID': self._agent_id, headers_mod.CONTENT_TYPE: headers_mod.CONTENT_TYPE.PLAIN_TEXT, headers_mod.DATE: now, } self.vip.pubsub.publish( 'pubsub','heartbeat/listeneragent', headers, now) vip.pubsub.publish is called with the following arguments: • ‘pubsub’ - Always the first argument to vip.pubsub.publish.

194 Chapter 7. Example Agents and Uitlities VOLTTRON Documentation, Release 3.5RC1

• topic - Topic of message or data. In this example the Listener is publishing to the ‘heartbeat/listeneragent’ topic • headers - Contains information such as date published, content type (plain text, utf-8, asci, etc), and the agent ID for publishing agent. • message - Can be a single value (float, integer, or string), list of objects, or dictionary. Agents subscribing to the message should know the format of message so they can parse and use the information. In this example the Listener agent is publishing a string message containing the current time (“now”) in UTC ISO format (note: Python datetime objects should be converted to strings before passing to vip.pubsub.publish).

7.1.5 Ping Pong Agent

The PingPongAgent demonstrates how to use the mobility feature within VOLTTRON. This simple agent moves back and forth between two VOLTTRON platforms. The agent will update a “count” file each time it moves to demonstrate the carrying of data along the agents journey. This agent code is available in: examples/PingPongAgent directory of the VOLTTRON platform. NOTE: In order for this agent to be successfully used the VOLTTRON-Restricted package must be installed and enabled. For information on this package see ‘VOLTTRON™ Restricted ‘__.

Configuration for the PingPongAgent

The PingPongAgent requires that the VOLTTRON-Restricted module be installed and that the mobility feature has been enabled (creation of keys to verify authorization for communication between platforms). For instructions on enabling the mobility feature, creating security keys, and adding these keys for communication with other authorized platforms, please see Agent Mobility section. The ping pong agent requires hosts and how often a request to move should be sent. Within the host list add the IP for the two VOLTTRON platforms and how often to move the agent from one platform to the other. The configuration file should appear similar to this example: Typical file location: ~/volttron/examples/PingPongAgent/config.json { "hosts":["1xx.x.x.1","1yy.y.y.1"], "period": 30 }

Packaging and launching the PingPongAgent

The following steps will show how to sign (VOLTTRON-Restricted agent verification feature) the PingPongAgent and launch the PingPongAgent. From a terminal, in the volttron directory, enter the following commands: 1. Ensure the platform is active: . env/bin/activate 2. Ensure that VOLTTRON is running on both platforms. Issue the following command on both platforms to start VOLTTRON: volttron -vv -l volttron.log& 3. Package the agent: volttron-pkg package Agents/PingPongAgent

7.1. Example Agents 195 VOLTTRON Documentation, Release 3.5RC1

4. Sign the agent as creator (resource_contract is a text file containing the hardware and software requirements for the agent, see Resource Monitor): volttron-pkg sign --creator --contract resource_contract ~/.volttron/packaged/pingpongagent-0.1-py2-none-any.whl 5. Sign the agent as admin: volttron-pkg sign --admin ~/.volttron/packaged/pingpongagent-0.1-py2-none-any.whl 6. Sign the agent as initiator: volttron-pkg sign --initiator --config-file examples/PingPongAgent/config.json ~/.volttron/packaged/pingpongagent-0.1-py2-none-any.whl 7. Set the configuration file: volttron-pkg configure ~/.volttron/packaged/pingpongagent-0.1-py2-none-any.whl examples/PingPongAgent/config.json 8. Install agent into platform (with the platform running): volttron-ctl install ~/.volttron/packaged/listeneragent-3.0-py2-none-any.whl 9. Check volttron status to verify agent is ready to start: volttron-ctl status 10. The terminal will have output that looks similar to: AGENT TAG STATUS 1f pingpongagent-0.1

11. Start the agent (1f is the window above is the unique agent identifier. This identifier will only be as long as necessary to ensure uniqueness on the running platform): volttron-ctl start 1f 12. Verify that agent is running: volttron-ctl status AGENT TAG STATUS 1f pingpongagent-0.1 running [pid]

In the run directory (VOLTTRON_HOME/run) one will see the incremented file appear and disappear as the PingPon- gAgent jumps back and forth between the two platforms.

PingPongAgent Code Rundown

For completeness the PingPongAgent code is detailed and explained below: import errno import logging import os import sys from volttron.platform.agent import BaseAgent, PublishMixin, periodic from volttron.platform.agent import utils, matching utils.setup_logging() _log = logging.getLogger(__name__)

196 Chapter 7. Example Agents and Uitlities VOLTTRON Documentation, Release 3.5RC1

def PingPongAgent(config_path, **kwargs): '''Agent to demonstrate agent-initiated mobility.

The agent periodically calculates the next host to visit from the hosts list specified in the config file and requests a move. '''

config = utils.load_config(config_path) period = config.get('period', 30) hosts = config['hosts'] uuid = os.environ['AGENT_UUID']

class Agent(PublishMixin, BaseAgent):

Handle failed move requests from the service bus @matching.match_glob('platform/move/reply/'+ uuid) def on_move_fail(self, topic, headers, message, match): error,= message _log.error('attempt to move %s failed: %s', uuid, error)

Uses a handy periodic decorator to specify that this method should be called over and over again. Each time the ‘move’ function is called the ‘count’ file is updated and a request to go to a different host is sent. If the request is accepted and the agent is moved then the agent will be shutdown and removed from the current platform. @periodic(period) def move(self): count=0 try: file= open('count','r') except IOError as exc: if exc.errno != errno.ENOENT: _log.error('error opening count file: %s', exc) return else: try: count= int(file.read().strip()) except ValueError: count=0 host= hosts[count% len(hosts)] with open('count','w') as file: file.write(str(count+1))

This is where the agent requests the platform to move if this move is successful the agent. self.publish('platform/move/request/'+ uuid, {}, host) Agent.__name__='PingPongAgent' return Agent(**kwargs)

Describe the agent to the platform. def main(argv=sys.argv): '''Main method called by the eggsecutable.''' try: utils.default_main(PingPongAgent, description='Example VOLTTRON™ mobility agent', argv=argv) except Exception as e: _log.exception('unhandled exception')

7.1. Example Agents 197 VOLTTRON Documentation, Release 3.5RC1

if __name__ =='__main__': # Entry point for script try: sys.exit(main()) except KeyboardInterrupt: pass

7.1.6 Scheduler Example

The SchedulerExampleAgent demonstrates how to use the scheduling feature of the [[ActuatorAgent]] as well as how to send a command. This agent publishes a request for a reservation on a (fake) device then takes an action when it’s scheduled time appears. The ActuatorAgent must be running to exercise this example. Note: Since there is no actual device, an error is produced when the agent attempts to take its action. def publish_schedule(self): '''Periodically publish a schedule request''' headers={ 'AgentID': agent_id, 'type':'NEW_SCHEDULE', 'requesterID': agent_id, #The name of the requesting agent. 'taskID': agent_id+"-ExampleTask", #The desired task ID for this task. It must be unique among all other scheduled tasks. 'priority':'LOW', #The desired task priority, must be 'HIGH', 'LOW', or 'LOW_PREEMPT' }

start= str(datetime.datetime.now()) end= str(datetime.datetime.now()+ datetime.timedelta(minutes=1))

msg=[ ['campus/building/unit',start,end] ] self.vip.pubsub.publish( 'pubsub', topics.ACTUATOR_SCHEDULE_REQUEST, headers, msg)

The agent listens to schedule announcements from the actuator and then issues a command @PubSub.subscribe('pubsub', topics.ACTUATOR_SCHEDULE_ANNOUNCE(campus='campus', building='building',unit='unit')) def actuate(self, peer, sender, bus, topic, headers, message): print ("response:",topic,headers,message) if headers[headers_mod.REQUESTER_ID] != agent_id: return '''Match the announce for our fake device with our ID Then take an action. Note, this command will fail since there is no actual device''' headers = { 'requesterID': agent_id, } self.vip.pubsub.publish( 'pubsub', topics.ACTUATOR_SET(campus='campus', building='building',unit='unit', point='point'),

198 Chapter 7. Example Agents and Uitlities VOLTTRON Documentation, Release 3.5RC1

7.2 Utilities

7.2.1 BaseAgent

The BaseAgent class subscribes to required topics, responds to platform messages, and provides hooks for application- specific logic. While it is not required, it is recommended that agents extend this class to ease development.

7.2.2 Data Publisher

The data publisher agent allows the playback of csv data to the message bus as if it were a device. { # basetopic can be devices, analysis, or custom base topic "basetopic":"devices", "publisherid":"PUBLISHER", "maintain_timestamp":1,

# Declare standard topic format that identifies campus, building, # device (unit), and subdevices (subdevices are optional) "campus":"campus1", "building":"building1", "unit":{ "rtu4":{ "subdevices": [] }, "rtu5":{ "subdevices": [] }, "rtu3":{ "subdevices": [] } }, "unittype_map":{ ".*Temperature":"Farenheit", ".*SetPoint":"Farenheit", "OutdoorDamperSignal":"On/Off", "SupplyFanStatus":"On/Off", "CoolingCall":"On/Off", "SupplyFanSpeed":"RPM", "Damper*.":"On/Off", "Heating*.":"On/Off", "DuctStatic*.":"On/Off" },

# If a custom topic is desired the entire topic must be configured. # e.g., "custom_topic": 'custom/topic/configuration' # "custom_topic": "input_file":"/home/volttron/shared/data.csv",

# Publish interval in seconds "publish_interval":1,

# Tell the playback to maintain the location in the file if playback stops # before the file ends. # default 0 "remember_playback":1,

7.2. Utilities 199 VOLTTRON Documentation, Release 3.5RC1

# Start playback from 0 even though remember playback may be set. # default 0 "reset_playback":0 }

7.2.3 Driven Agents

Configuration for running openeis applications within volttron.

The configuration of an agent within volttron requires a small modification to the imports of the openeis application and a couple of configuration parameters.

Import and Extend from volttron.platform.agent import (AbstractDrivenAgent, Results) ... class OpeneisApp(AbstractDrivenAgent):

Configuration

The two parameters that are necessary in the json configuration file are “application” and “device”. An optional but recommended argument should also be added “agentid”. { "agentid": "drivenlogger1", "application": "drivenlogger.logdevice.LogDevice", "device": "pnnl/isb1/oat", ... }

Any other keys will be passed on to the openeis application when it is run.

7.2.4 MultiBuilding Agent

This agent has been superseded by the VIP functionality introduced in 3.0 and should be considered deprecated. However it is still a usable agent. Multi-building (or multi-node) messaging is implemented as a service-style agent. Its use is optional and it can be enabled/disabled by simply enabling/disabling the multibuilding service agent. It is easily configured using the service configuration file and provides several new topics for use in the local agent exchange bus.

Configuration

The service configuration file may contain the declarations below: • building-publish-address: A ØMQ address on which to listen for messages published by other nodes. Defaults to ‘tcp://0.0.0.0:9161‘. • building-subscribe-address:

200 Chapter 7. Example Agents and Uitlities VOLTTRON Documentation, Release 3.5RC1

A ØMQ address on which to listen for messages subscribed to by other nodes. Defaults to ‘tcp://0.0.0.0:9160‘. • public-key, secret-key: Curve keypair (create with zmq.curve_keypair()) to use for authentication and encryption. If not provided, all communications will be unauthenticated and unencrypted. • hosts: A mapping (dictionary) of building names to publish/subscribe addresses. Each entry is of the form: "CAMPUS/BUILDING": {"pub": "PUB_ADDRESS", "sub": "SUB_ADDRESS", "public-key": "PUBKEY", "allow": "PUB_OR_SUB"}

• CAMPUS/BUILDING: building for which the given parameters apply • PUB_ADDRESS: ØMQ address used to connect to the building for publishing • SUB_ADDRESS: ØMQ address used to connect to the building subscriptions • PUBKEY: curve public key of the host used to authenticate incoming connections • PUB_OR_SUB: the string “pub” to allow publishing only or “sub” to allow both publish and subscribe • cleanup-period: Frequency, in seconds, to check for and close stale connections. Defaults to 600 seconds (10 minutes). • uuid: A UUID to use in the Cookie header. If not given, one will be automatically generated.

Sending and Receiving Inter-building Messages

Three topics are provided for inter-building messaging: • building/recv/CAMPUS/BUILDING/TOPIC: Agents can subscribe to to this topic to receive messages sent to TOPIC at the building specified by CAMPUS/BUILDING. • building/send/CAMPUS/BUILDING/TOPIC: Agents can send messages to this topic to have them forwarded to TOPIC at the building specified by CAMPUS/BUILDING. • building/error/CAMPUS/BUILDING/TOPIC: Errors encountered during sending/receiving to/from the above two topics will be sent over this topic.

Limitations and Future Work

• Requires opening multiple listening ports: It would be nice to multiplex all inter-building communications over a single port to decrease the attack footprint and ease firewall administration. • There is limited authorization: a remote host can either publish or publish and subscribe. Perhaps a filter list can be included to limit which topics a host may subscribe to or publish on. • Remote host lookup is kept in a static file:

7.2. Utilities 201 VOLTTRON Documentation, Release 3.5RC1

Ideally, host lookup would be done through some central directory service, but that is not currently implemented.

7.2.5 Multinode Example

The MultiNode example agent demonstrates how to setup and make use of the [[MultiBuildingMessaging]] agent. For convenience, this example is setup to be run from a single machine but could be easily modified to run off multiple systems. Multiple instances of VOLTTRON can be run on a single machine with the proper configuration. For this example, two separate VOLTTRON homes are setup and the MultiBuilding service binds to different local addresses. [[PlatformConfiguration]] shows how the VOLTTRON_HOME is used for platform directories. The example agent directory contains the config files for the example agents and the multibuilding agents. Each is setup to know about the other platform instance and contains its own pub and sub addresses. Please see [[MultiBuild- ingMessaging]] for details on the configuration file. MultiBuilding config: { "building-publish-address":"tcp://127.0.0.1:12201", "building-subscribe-address":"tcp://127.0.0.1:12202", "uuid":"MultiBuildingService", "hosts":{ "campus/platform1":{ "pub":"tcp://127.0.0.1:12201", "sub":"tcp://127.0.0.1:12202" }, "campus/platform2":{ "pub":"tcp://127.0.1.1:12201", "sub":"tcp://127.0.1.1:12202" } } }

Each GreeterAgent is setup with the other hosts it will be publishing to in the publish_heartbeat method. GreeterAgent config: { "agentid":"Greeter1", "receiving_platforms":["platform2"] }

In order to run this example: • First activate the platform: . env/bin/activate

• Then, create the directories which will be used by each platform as its VOLTTRON_HOME: mkdir ~/.platform1 mkdir ~/.platform2

• Start the first platform: VOLTTRON_HOME=~/.platform1 volttron -vv -l platform1.log&

• Build, configure, and install the multibuilding platform agent. The Agent is installed with “multinode=” to tag the agent at the same time it is installed. This is a convenient way to refer to the agent later.

202 Chapter 7. Example Agents and Uitlities VOLTTRON Documentation, Release 3.5RC1

VOLTTRON_HOME=~/.platform1 volttron-pkg package Agents/MultiBuilding VOLTTRON_HOME=~/.platform1 volttron-pkg configure ~/.volttron/packaged/multibuildingagent-0.1-py2-none-any.whl Agents/MultiNodeExample/multicomm.service VOLTTRON_HOME=~/.platform1 volttron-ctl install multinode=~/.volttron/packaged/multibuildingagent-0.1-py2-none-any.whl

• Build, configure, and install the GreeterAgent VOLTTRON_HOME=~/.platform1 volttron-pkg package Agents/MultiNodeExample VOLTTRON_HOME=~/.platform1 volttron-pkg configure ~/.volttron/packaged/greeteragent-0.1-py2-none-any.whl Agents/MultiNodeExample/agent1.config VOLTTRON_HOME=~/.platform1 volttron-ctl install greeter=~/.volttron/packaged/greeteragent-0.1-py2-none-any.whl

• Start the second platform: VOLTTRON_HOME=~/.platform2 volttron -vv -l platform2.log&

• Build, configure, and install the MultiBuilding service for the second platform VOLTTRON_HOME=~/.platform2 volttron-pkg package Agents/MultiBuilding VOLTTRON_HOME=~/.platform2 volttron-pkg configure ~/.volttron/packaged/multibuildingagent-0.1-py2-none-any.whl Agents/MultiNodeExample/multicomm2.service VOLTTRON_HOME=~/.platform2 volttron-ctl install multinode=~/.volttron/packaged/multibuildingagent-0.1-py2-none-any.whl

• Build, configure, and install the GreeterAgent for the second platform VOLTTRON_HOME=~/.platform2 volttron-pkg package Agents/MultiNodeExample VOLTTRON_HOME=~/.platform2 volttron-pkg configure ~/.volttron/packaged/greeteragent-0.1-py2-none-any.whl Agents/MultiNodeExample/agent2.config VOLTTRON_HOME=~/.platform2 volttron-ctl install greeter=~/.volttron/packaged/greeteragent-0.1-py2-none-any.whl

• Start up the agents on both platforms by referring to them by the tag they were installed with VOLTTRON_HOME=~/.platform1 volttron-ctl start --tag multinode VOLTTRON_HOME=~/.platform1 volttron-ctl start --tag greeter VOLTTRON_HOME=~/.platform2 volttron-ctl start --tag multinode VOLTTRON_HOME=~/.platform2 volttron-ctl start --tag greeter

• Check the logs for each platform for the presence of messages from the other platform’s GreeterAgent grep Greeter2 platform1.log -a

2014-09-30 17:13:41,840 (greeteragent-0.1 13878) greeter.agent DEBUG: Topic: greetings/hello, Headers: Headers({u’Date’: u’2014-10-01 00:13:41.831539Z’, u’Cookie’: u’Greeter2’, u’AgentID’: u’Greeter2’, u’Content-Type’: [u’application/json’]}), Message: [’{"message":"HELLO from Greeter2!"}’]

grep Greeter1 platform2.log -a

7.2.6 Process Agent

This agent can be used to launch non-Python agents in the VOLTTRON platform. The agent handles keeping track of the process so that it can be started and stoped with platform commands. Edit the configuration file to specify how to launch your process. This agent was originally created for launching sMAP along with the platform, but can be used for any process. Note: Currently this agent does not respond to a blanket “shutdown” request and must be stopped with the “stop” command.

7.2. Utilities 203 VOLTTRON Documentation, Release 3.5RC1

7.2.7 Scripts

In order to make repetitive tasks less repetitive the VOLTTRON team has create several scripts in order to help. These tasks are available in the scripts directory. Before using these scripts you should become familiar with the Agent Development process. In addition to the scripts directory, the VOLTTRON team has added the config directory to the .gitignore file. By convention this is where we store customized scripts and configuration that will not be made public. Please feel free to use this convention in your own processes. The scripts/core directory is laid out in such a way that we can build scripts on top of a base core. For example the scripts in sub-folders such as the management-service-demo, historian-scripts, and demo-commsa all use the scripts that are present in the core directory. The most widely used script is scripts/core/pack_install.sh. The pack_install.sh script will remove an agent if the tag is already present, create a new agent package, and install the agent to VOLTTRON_HOME. This script has three required arguments and has the following signature # Agent to Package must have a setup.py in the root of the directory. scripts/core/pack_install.sh

The pack_install.sh script will respect the VOLTTRON_HOME specified on the command line or set in the global environment. An example of setting VOLTTRON_HOME is as follows. # Sets VOLTTRON_HOME to /tmp/v1home VOLTTRON_HOME=/tmp/v1home scripts/core/pack_install.sh

Use the following scripts as examples that can be modified for your own agents. • scripts/core/make-listener can be modified for any agent and make it one command to stop, remove, build, install, configure, tag, start, and (optionally) enable an agent for autostart. Fill out the script with the location of the agent source, config file, and tag name. The optional parameter enable can be passed to the make-agent script to set the agent to autostart with the platform. • make-listener-enc-auth is similar to make-listener but uses encryption and authentication.

204 Chapter 7. Example Agents and Uitlities CHAPTER 8

Scalability Experiments

8.1 Scalability Setup

8.1.1 Core Platform

• VIP router - how many messages per second can the router pass • A single agent can connect and send messages to itself as quickly as possible • Repeat but with multiple agents • Maybe just increase the number of connected but inactive agents to test lookup times • Inject faults to test impact of error handling • Agents • How many can be started on a single platform? • How does it affect memory? • How is CPU affected?

8.1.2 Socket types

• inproc - lockless, copy-free, fast • ipc - local, reliable, fast • tcp - remote, less reliable, possibly much slower • test with different – latency – throughput – jitter (packet delay variation) – error rate

8.1.3 Subsystems

• ping - simple protocol which can provide baseline for other subsystems

205 VOLTTRON Documentation, Release 3.5RC1

• RPC - requests per second • Pub/Sub - messages per second • How does it scale as subscribers are added

8.1.4 Core Services

• historian • How many records can be processed per second? • drivers • BACnet drivers use a virtual BACnet device as a proxy to do device communication. Currently there is no known upper limit to the number of devices that can be handled at once. The BACnet proxy opens a single UDP port to do all communication. In theory the upper limit is the point when UDP packets begin to be lost due to network congestion. In practice we have communicated with ~190 devices at once without issue. • ModBUS opens up a TCP connection for each communication with a device and then closes it when finished. This has the potential to hit the limit for open file descriptors available to the master driver process. (Before, each driver would run in a separate process, but that quickly uses up sockets available to the platform.) To protect from this the master driver process raises the total allowed open sockets to the hard limit. The number of concurrently open sockets is throttled at 80% of the max sockets. On most Linux systems this is about 3200. Once that limit is hit additional device communications will have to wait in line for a socket to become available.

8.1.5 Tweaking tests

• Configure message size • Perform with/without encryption • Perform with/without authentication

8.1.6 Hardware profiling

• Perform tests on hardware of varying resources: Raspberry Pi, NUC, Desktop, etc.

8.1.7 Scenarios

• One platform controlling large numbers of devices • One platform managing large numbers of platforms • Peer communication (Hardware demo type setup)

8.1.8 Impact on Platform

What is the impact of a large number of devices being scraped on a platform (and how does it scale with the hardware)? • Historians • At what point are historians unable to keep up with the traffic being generated? • Is the bottleneck the sqlite cache or the specific implementation (SQLite, MySQL, sMAP) • Do historian queues grow so large we have a memory problem?

206 Chapter 8. Scalability Experiments VOLTTRON Documentation, Release 3.5RC1

• Large number of devices with small number of points vs small number of devices with large number of points • How does a large message flow affect the router? • Examine effects of the watermark (does increasing help) • Response time for volttron-ctl commands (for instance: status) • Affect on round trip times (Agent A sends message, Agent B replies, Agent A receives reply) • Do messages get lost at some point (EAgain error)? • What impact does security have? Are things significantly faster in developer-mode? • Regulation Agent Every 10 minutes there is an action the master node determines. Duty cycle cannot be faster than that but is set to 2 seconds for simulation. | Some clients miss duty cycle signal | Mathematically each node solves ODE. | Model notes accept switch on/off from master. | Bad to lose connection to clients in the field Chaos router to introduce delays and dropped packets. MasterNode needs to have vip address of clients. Experiment capture historian - not listening to devices, just capturing results • Go straight to db to see how far behind other historians

8.2 Improvements Based on Results

Here is the list of scalability improvements so far: Reduced the overhead of the base historian by removing unneeded writes to the backup db. Significantly improved performance on low end devices. Added options to reduce the number of publishes per device per scrape. Common cases where per point publishes and breadth first topics are not needed the driver can be configured only publish the depth first “all” or any combination per device the operator needs. This dramatically decreases the platform hardware requirements while increasing the number of devices that can be scraped. Added support for staggering device scrapes to reduce CPU load during a scrape. Further ideas:

Determine if how we use ZMQ is reducing its efficiency. Remove an unneeded index in historian backup db. Increase backup db page count.

8.3 Scalability Planning

8.3.1 Goals

• Determine the limits of the number of devices that can be interacted with via a single Volttron platform. • Determine how scaling out affects the rate at which devices are scraped. i.e. How long from the first device scrape to the last?

8.2. Improvements Based on Results 207 VOLTTRON Documentation, Release 3.5RC1

• Determine the effects of socket throttling in the master driver on the performance of Modbus device scraping. • Measure total memory consumption of the Master Driver Agent at scale. • Measure how well the base history agent and one or more of the concrete agents handle a large amount of data. • Determine the volume of messages that can be achieved on the pubsub before the platform starts rejecting them.

8.3.2 Test framework

Test Devices

Simple, command line configured virtual devices to test against in both Modbus and BACnet flavors. Devices should create 10 points to read that generate either random or easily predictable (but not necessarily constant) data. Process should be completely self contained. Test devices will be run on remote hosts from the Volttron test deployment.

Launcher Script

• The script will be configurable as to the number and type of devices to launch. • The script will be configurable as to the hosts to launch virtual devices on. • The script (probably a fabric script) will push out code for and launch one or more test devices on one or more machines for the platform to scrape. • The script will generate all of the master driver configuration files to launch the master driver. • The script may launch the master driver. • The script may launch any other agents used to measure performance.

Shutdown Script

• The script (probably the same fabric script run with different options) will shutdown all virtual drivers on the network. • The script may shutdown the master driver. • The script may shutdown any related agents.

Performance Metrics Agent

This agent will track the publishes by the different drivers and generate data in some form to indicate: • Total time for all devices to be scraped • Any devices that were not successfully scraped. • Performance of the message bus.

Additional Benefits

Most parts of a test bed run should be configurable. If a user wanted to verify that the Master Driver worked, for instance, they could run the test bed with only a few virtual device to confirm that the platform is working correctly.

208 Chapter 8. Scalability Experiments VOLTTRON Documentation, Release 3.5RC1

Running a simple test

You will need 2 open terminals to run this test. (3 if you want to run the platform in it’s own terminal) | Checkout the feature/scalability branch. Start the platform. Go to the volttron/scripts/scalability-testing directory in two different terminals. (Both with the environment activated) In one terminal run: python config_builder.py --count=1500 --scalability-test --scalability-test-iterations=6 fake fake18.csv localhost

Change the path to fake.csv as needed. (Optional) After it finishes run: ./launch_fake_historian.sh to start the null historian. In a separate terminal run: ./launch_scalability_drivers.sh to start the scalability test. This will emulate the scraping of 1500 devices with 18 points each 6 times, log the timing, and quit. Redirecting the driver log output to a file can help improve performance. Testing should be done with and without the null historian. Currently only the depth first all is published by drivers in this branch. Uncomment the other publishes in driver.py to test out full publishing. fake.csv has 18 points. Optionally you can run two listener agents from the volttron/scripts directory in two more terminals with the command: ./launch_listener.sh and rerun the test to see the how it changes the performance.

8.3.3 Real Driver Benchmarking

Scalability testing using actual MODBUS or BACnet drivers can be done using the virtual device applications in the scripts/scalability-testing/virtual-drivers/ directory. The configuration of the master driver and launching of these virtual devices on a target machine can be done automatically with fabric.

Setup

This requires two computers to run: One for the VOLTTRON platform to run the tests on (“the platform”) and a target machine to host the virtual devices (“the target”).

Target setup

The target machine must have the VOLTTRON source with the feature/scalability branch checked out and boot- strapped. Make a note of the directory of the VOLTTRON code.

8.3. Scalability Planning 209 VOLTTRON Documentation, Release 3.5RC1

Platform setup

With the VOLTTRON environment activated install fabric. pip install fabric

Edit the file scripts/scalability-testing/test_settings.py as needed. • virtual_device_host (string) - Login name and IP address of the target machine. This is used to remotely start and stop virtual devices via ssh. “[email protected]“ • device_types - map of driver types to tuple of the device count and registry config to use for the virtual devices. Valid device types are “bacnet” and “modbus”. • volttron_install - location of volttron code on the target. To configure the driver on the platform and launch the virtual devices on the target run fab deploy_virtual_devices

When prompted enter the password for the target machine. Upon completion virtual devices will be running on the target and configuration files written for the master driver.

Launch Test

If your test includes virtual BACnet devices be sure to configure and launch the BACnet Proxy before launching the scalability driver test. (Optional) ./launch_fake_historian.sh to start the null historian. In a separate terminal run: ./launch_scalability_drivers.sh to start the scalability test. To stop the virtual devices run fab stop_virtual_devices and enter the user password when prompted.

210 Chapter 8. Scalability Experiments CHAPTER 9

Development History and Roadmap

9.1 Migration from 1.2 - 2.0

The most significant changes to the base VOLTTRON platform are the set of commands for controlling the platform and the way agents are managed. Existing agent code does not need to be modified except in cases of hardcoded paths and some imports. The way agents are packaged has changed but that does not change the setup.py file or any configuration agents were using. Summary of changes: • “lite” has been removed from the code tree. For packages, “lite” has been replaced by “platform”. • The agents are no longer built as eggs but are instead built as Python wheels • There is a new package command instead of using a script to build an egg • Agents are no longer installed with a 2 step process of “install-executable” and “load-agent”. Now the agent package is configured then installed. • Agents are no longer distinguished by their configuration files but can by a platform provided uuid and/or a user supplied tag. • The base topic for publishing data from devices is no longer “RTU” but “devices” • Application configuration files no longer need to contain the “exec” information. For an example of launching a non-Python agent, please see ProcessAgent

9.2 Migration from 2.x to 3.x

If you are upgrading an existing 2.0 installation, there are a few manual steps. From the project directory in unactivated mode: • rm -r env • rm -r volttron/platform/control • python bootstrap.py An overview of changes can be found at: VOLTTRON Primer Overview

211 VOLTTRON Documentation, Release 3.5RC1

9.2.1 Drivers

Drivers are no longer tied to smap. Please see the drivers page. sMAP Driver INI File

Previously, all driver setup was done in an smap.ini file with sections for each device. Now, this setup is done in two parts: the Master Driver Agent and individual drivers. The sections from the smap ini are now contained in their own files. These files are tied together by the master-driver.config: { "agentid":"master_driver", "driver_config_list":[ "/home/volttron/git/config/bacnet-device1.config", "/home/volttron/git/config/bacnet-device2.config" ] }

The following portion of the file is no longer needed for the driver but could be used to setup an sMAP Historian [report 0] ReportDeliveryLocation = http://\/backend/add/\

[/datalogger] type = volttron.drivers.data_logger.DataLogger interval = 1

Setting up paths for the collection and devices are now handled in the driver config file: [/] type = Collection Metadata/SourceName = MySource uuid =

[/Campus] type = Collection Metadata/Location/Campus = My Campus

[/Campus/Building] type = Collection Metadata/Location/Building = Building

[/Campus/Building/device] type = volttron.drivers.bacnet.BACnet target_address = IP self_address = IP:PORT interval = 60 Metadata/Instrument/Manufacturer = Manufacturer Metadata/Instrument/ModelName = Model Name register_config = /home/volttron/git/volttron/config/my-bacnet-config.csv

Becomes bacnet-device1.config: { "driver_config":{ "device_address":"13200:56" }, "campus":"Campus",

212 Chapter 9. Development History and Roadmap VOLTTRON Documentation, Release 3.5RC1

"building":"Building", "unit":"Device", "driver_type":"bacnet", "registry_config":"/home/volttron/git/volttron/config/my-bacnet-config.csv", "interval": 60, "timezone":"US/Pacific" }

Register Files (CSV)

These files are almost unchanged from v2.0. The sole change is the renaming of “PNNL Point Name” to “Volttron Point Name” This was a legacy label from the initial version of the platform and has now been updated. Point Name,PNNL Point Name,Units,Unit Details,BACnet Object Type,Property,Writable,Index,Notes Becomes: Point Name,Volttron Point Name,Units,Unit Details,BACnet Object Type,Property,Writable,Index,Notes The rest of the file remains the same.

9.2.2 Historian

Please look through the page [[Historian|VOLTTRON-Historians]] to see the support storage solutions. sMAP can still be used but is now optional.

9.2.3 ActuatorAgent

The Actuator can now be accessed via RPC which greatly simplifies the code needed to work with devices. The following shows how the old SchedulerExample agent was upgraded. The use_rpc method contains examples for replacing all the code for the pubsub interaction.

9.2.4 Agents

9.3 3.X drivers

9.3.1 Changes from v2.X

• PNNL Point Name is now: Volttron Point Name • Drivers are now agents • No more smap config file, now it is an Agent config file. • MODBUS, add port argument to driver_config dictionary • Agent config file has links to driver config files which have links to driver register file. Edit the master driver config. This points to the configuration files for specific drivers. Each of these drivers uses a CSV file to specify their points (registry file).

9.3. 3.X drivers 213 VOLTTRON Documentation, Release 3.5RC1

9.3.2 Master Driver Config

• agentid - name of agent • driver_config_list - list of configuration files for drivers under this master

{ “agentid”: “master_driver”, “driver_config_list”: [

“/home/user/git/volttron/services/core/MasterDriverAgent/master_driver/test_modbus1.config” | ] | }

9.3.3 Device Driver Config

• driver_config - driver specific information, modbus just needs the ip for the device being controlled • campus/building/unit - path to the device • driver_type - specify the type of driver (modbus, bacnet, custom) • registry_config - the registry file specifying points to collect • interval - how often to grab/publish data • timezone - TZ of data being collected • heart_beat_point - registry point to use as a hearbeat to indicate that VOLTTRON is still controlling device

{ “driver_config”: {“device_address”: “”, “proxy_address”: “9f18c8d7-ec4b-4674-ad49-e7d0d3328f99”}, “campus”: “campus”, “building”: “building”, “unit”: “bacnet1”, “driver_type”: “bacnet”,

“registry_config”:”/home/user/git/volttron/volttron/drivers/bacnet_lab.csv”, | “interval”: 5, | “timezone”: “UTC” | }

9.4 Migration from 3.0 to 3.5

9.4.1 Drivers

The BACnet driver configurations now require device ids. 3.0 configurations had the line:

214 Chapter 9. Development History and Roadmap VOLTTRON Documentation, Release 3.5RC1

"driver_config": {"device_address": address},

3.5 configs needs the following addition to the the driver_config dictionary: "driver_config": {"device_address": address, "device_id": id},

9.4.2 Historian

The 3.5 MySQL historian will try adding rows to a metadata table but will not create the table automatically. It can be added to the database with CREATE TABLE meta(topic_id INTEGER NOT NULL, metadata TEXT NOT NULL, PRIMARY KEY(topic_id));

9.4.3 ActuatorAgent

The Heartbeat agent has been removed in version 3.5, its job now being done from within the actuator. The period of the heartbeat toggle function can be set by adding "heartbeat_period": 20 to the actuator’s config file. This period defaults to 60 seconds if it is not specified.

9.5 Change Logs

9.5.1 1/31/2014

The VOLTTRON(tm) 1.0 release includes the following features: • Scheduler 2.0: The new ActuatorAgent scheduler allows applications to reserve devices ahead of time • SchedulerExample: This simple agent provides an example of publishing a schedule request. VOLTTRON v1.0 also includes features in a beta stage. These features are slated for release in v1.1 but are included in 1.0 for those who wish to begin investigating them. These features are: • Multi-node communication: Enables platforms to publish and subscribe to each other • BACNet Driver: Enables reading/writing to devices using the BACNet protocol Included are PNNL developed applications: AFDD and DR which are in the process of being modified to work with the new scheduler.DR will not currently function with Scheduler 2.0.

9.5.2 11/7/2013

• Renamed Catalyst driver to Modbus driver to reflect the generic nature of the driver. • Changed the configuration for the driver to fully take advantage of the Python struct module.

9.5. Change Logs 215 VOLTTRON Documentation, Release 3.5RC1

9.5.3 9/9/2013

• Catalyst registry file update for 372s • catalystreg.csv.371 contains the points for the 371

9.5.4 9/4/2013

• Scheduling implemented • Logging implemented

9.5.5 8/21/2013

• Added libevent-dev to required software

9.5.6 8/6/2013

WeatherAgent updated and back into the repository.

9.5.7 7/22/2017

The agent module was split into multiple pieces. • The BaseAgent and PublishMixin classes and the periodic decorator remain in the agent package. • The matching module was moved under the agent package and is now available as volttron.lite.agent.matching. • The utility functions, like run_agent (which is deprecated) and the base agent ArgumentParser, were moved to volttron.lite.agent.utils. All low-level messaging that is not agent-specific was moved to volttron.lite.messaging and includes the following new submodules: • headers: contains common messaging headers, like CONTENT_TYPE, and values as constants • topics: provides topic templates; see the module documentation for details • utils: includes the Topic class and other messaging/topic utilities The listener, control, archiver, and actuator agents were updated to use and demonstrate the changes above. Some of them also show how to use agent factories to perform dynamic matching. Using mercurial to show the diffs between revisions is a good technique for others to use to investigate how to migrate their agents.

9.5.8 6/24/2013

• Initial version of ExampleControllerAgent committed. This agent monitors outdoor air temp and randomly sets the coolsuppy fan if temp has risen since the last reading. Wiki explanation for agent coming soon. • Updates to ActuatorAgent • ListenerAgent updated to reflect latest BaseAgent • Use -config option instead of -config_path when starting agents

216 Chapter 9. Development History and Roadmap VOLTTRON Documentation, Release 3.5RC1

9.5.9 6/21/2013

• Updated ArchiverAgent checked in. • ActuatorAgent for sending commands to the controller checked in.

9.5.10 6/19/2013

• Fixed a command line arg problem in ListenerAgent and updated wiki.

9.5.11 Version 1.0

This is the initial release of the Volttron Lite platform. The features contained in it are: • Scripts for building the platform from scratch as well as updating • A BaseAgent which expresses the basic functionality for an agent in the platform as well as hooks for adding functionality • Example agents which utilize the BaseAgent to illustrate more complex behavior In addition, this wiki will be constantly updated with documentation for working with the platform and developing agents in it. We intend to document as much as possible but please submit TRAC tickets in cases where documentation does not exist yet or there is difficulty locating it. Also, this is a living document so feel free to add your own content to this wiki and even make changes to the documentation if you can improve on its clarity and usefulness. Please subscribe to this page to receive notification when new changelogs are posted for future releases.

9.6 Transitioning from 1.x to 2.x

VOLTTRON(tm) 2.0 introduces new features such as agent packaging/verification, agent mobility, and agent resource monitoring. In addition, some existing features from 1.2 have been refactored. These changes are mostly confined to platform administration and should require minimal changes to existing agents aside from fixing imports and any hardcoded paths/topics in the code. • “lite” has been removed from the code tree. For packages, “lite” has been replaced by “platform”. • The agents are no longer built as eggs but are instead built as Python wheels • There is a new package command instead of using a script to build an egg • Agents are no longer installed with a 2 step process of “install-executable” and “load-agent”. Now the agent package is configured then installed. • Agents are no longer distinguished by their configuration files but by a platform provided uuid and/or a user supplied tag. • The base topic for publishing data from devices is no longer “RTU” but “devices” The most visible changes have been to the platform commands for building and managing agents. Please see [Plat- formCommands] (PlatformCommands “wikilink”) for these changes.

9.6. Transitioning from 1.x to 2.x 217 VOLTTRON Documentation, Release 3.5RC1

9.7 VOLTTRON Versions

9.7.1 VOLTTRON 1.0 - 1.2

• VOLTTRON platform based on PNNL research and needs of the RTU Network project • Open Source Reimplementation omitting patented features • Integrates researcher applications, devices, and cloud applications and resources • 1.0 Focused on building up the framework • Agent execution environment • Basic platform services • Modbus driver • 1.2 Expanded capabilities of platform • BACnet support • Multi-node communication • Released on GitHub

9.7.2 VOLTTRON 2.0

• 2.0 Incorporated PNNL IP from the original research • Different license: Free for buildings domain • Resource monitoring • Agents must present an execution contract to the platform stating their resource requirements • Platform rejects agents which it cannot support • Expandable framework for specify additional resources • Agent signing and verification • Agent package contains multiple layers which can be signed by different entities • Creator of code • Administrator of ‘Scope of Influence’/Deployment • Instantiator of agent • Most recent platform (for mobile agents) • Each level verified before agent is allowed to run • Entities cannot change content of other layers • Agent Mobility • Admin can send an agent to another platform for deployment/updating • Agent can request to move • Agent can bring along working files as part of ‘mutable luggage’ • Receiving platform verifies agent package and examines resource contract before executing agent

218 Chapter 9. Development History and Roadmap VOLTTRON Documentation, Release 3.5RC1

9.7.3 VOLTTRON 3.0

• Modularized Historian • Historians can be built for any storage solution – Previous versions did not have option for local storage • BaseHistorian – Can be extended for any solution – Handles subscribing to Bus – Local cache • Modularized Drivers • Standardized creating custom drivers to scrape data and publish to the message bus • Simplify developing drivers and contributing new capabilities back to VOLTTRON • Abstracted out driver interfaces allowing Actuator Agent to handle controlling devices via any protocol • VIP - VOLTTRON Interconnect Protocol • Increase security of the message bus and allow direct communication where appropriate • New communication model underneath VOLTTRON Message Bus • Compatibility layer so changes are transparent to existing agents • Platform Agent • Provides point of contact for the platform • Enables VOLTTRON Management Central control of platform • VOLTTRON Management Central • Web interface for administering VOLTTRON platforms in deployment

9.7. VOLTTRON Versions 219 VOLTTRON Documentation, Release 3.5RC1

220 Chapter 9. Development History and Roadmap CHAPTER 10

API Documentation

10.1 Services

10.1.1 ActuatorAgent actuator package

Submodules actuator.agent module

The Actuator Agent is used to manage write access to devices. Other agents may request scheduled times, called Tasks, to interact with one or more devices. Agents may interact with the ActuatorAgent via either PUB/SUB or RPC, but it is recommended agents use RPC to interact with the ActuatorAgent. The PUB/SUB interface remains primarily for VOLTTRON 2.0 agents. The Actuator Agent also triggers the heart beat on devices whose drivers are configured to do so.

ActuatorAgent Configuration “schedule_publish_interval” Interval between published schedule announcements in seconds. De- faults to 30. See Schedule State Publishes. “preempt_grace_time” Minimum time given to Tasks which have been preempted to clean up in sec- onds. Defaults to 60. “schedule_state_file” File used to save and restore Task states if the ActuatorAgent restarts for any rea- son. File will be created if it does not exist when it is needed. “heartbeat_period” How often to send a heartbeat signal to all devices in seconds. Defaults to 60.

Sample configuration file { "schedule_publish_interval": 30, "schedule_state_file":"actuator_state.pickle" }

221 VOLTTRON Documentation, Release 3.5RC1

Workflow Agents interact with the Actuator Agent following these basic steps: • Schedule one or more blocks of time with one or more devices. This is called a Task. • If needed wait until a block of time starts. • Set one or more values on the reserved devices. • Cancel the schedule when finished.

Scheduling a New Task RPC interface PUB/SUB interface Creating a Task requires four things: • The requester of the Task. This is the Agent’s ID. • A name for the Task. • The Task’s priority. • A list of devices and time ranges for each device.

Task Priority There are three valid prioirity levels: “HIGH” This Task cannot be preempted under any circumstance. This Task may preempt other conflict- ing preemptable Tasks. “LOW” This Task cannot be preempted once it has started. A Task is considered started once the earliest time slot on any device has been reached. This Task may not preempt other Tasks. “LOW_PREEMPT” This Task may be preempted at any time. If the Task is preempted once it has begun running any current time slots will be given a grace period (configurable in the ActuatorAgent configuration file, defaults to 60 seconds) before being revoked. This Task may not preempt other Tasks. Whenever a Task is preempted the Actuator Agent will publish a message to devices/actuators/schedule/result indicating that the Task has been cancelled due to being pre- empted. See Preemption Publishes Even when using the RPC interface agents which schedule low priority tasks may need to subscribe to devices/actuators/schedule/result to learn when its Tasks are canceled due to preemption.

Device Schedule The device schedule is a list of block of time for each device. Both the RPC and PUB/SUB interface accept schedules in the following format: [ ["campus/building/device1", #First time slot. "2013-12-06 16:00:00", #Start of time slot. "2013-12-06 16:20:00"], #End of time slot. ["campus/building/device1", #Second time slot. "2013-12-06 18:00:00", #Start of time slot. "2013-12-06 18:20:00"], #End of time slot. ["campus/building/device2", #Third time slot. "2013-12-06 16:00:00", #Start of time slot. "2013-12-06 16:20:00"], #End of time slot. #etc... ]

Note: Points on Task Scheduling

222 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

• Task id and requester id (agentid) should be a non empty value of type string • A Task schedule must have at least one time slot. • The start and end times are parsed with dateutil’s date/time parser. The default string representation of a python datetime object will parse without issue. • Two Tasks are considered conflicted if at least one time slot on a device from one task overlaps the time slot of the other on the same device. • The end time of one time slot can be the same as the start time of another time slot for the same device. This will not be considered a conflict. For example, time_slot1(device0, time1, time2) and time_slot2(device0,time2, time3) are not considered a conflict • A request must not conflict with itself.

New Task Response Both the RPC and PUB/SUB interface respond to requests with the result in the following format: { 'result':<'SUCCESS','FAILURE'>, 'info':, 'data': }

The PUB/SUB interface will respond to requests on the devices/actuators/schedule/result topic. The PUB/SUB interface responses will have the following header: { 'type':'NEW_SCHEDULE' 'requesterID':, 'taskID': }

Failure Reasons In many cases the ActuatorAgent will try to give good feedback as to why a request failed. The type of failure will populate “info” item as a string. Some of these errors only apply to the PUB/SUB interface.

General Failures “INVALID_REQUEST_TYPE” Request type was not ”NEW_SCHEDULE” or ”CANCEL_SCHEDULE”. “MISSING_TASK_ID” Failed to supply a taskID. “MISSING_AGENT_ID” AgentID not supplied.

Task Schedule Failures “TASK_ID_ALREADY_EXISTS ” The supplied taskID already belongs to an existing task. “MISSING_PRIORITY” Failed to supply a priority for a Task schedule request. “INVALID_PRIORITY” Priority not one of ”HIGH”, ”LOW”, or ”LOW_PREEMPT”. “MALFORMED_REQUEST_EMPTY” Request list is missing or empty. “REQUEST_CONFLICTS_WITH_SELF” Requested time slots on the same device overlap.

10.1. Services 223 VOLTTRON Documentation, Release 3.5RC1

“MALFORMED_REQUEST” Reported when the request parser raises an unhandled exception. The excep- tion name and info are appended to this info string. “CONFLICTS_WITH_EXISTING_SCHEDULES” This schedule conflict with an existing schedules that it can- not preempt. The data item for the results will contain info about the conflicts in this form: { '': { '': [ ["campus/building/device1", "2013-12-06 16:00:00", "2013-12-06 16:20:00"], ["campus/building/device1", "2013-12-06 18:00:00", "2013-12-06 18:20:00"] ] '':[...] } '':{...} }

Device Interaction

Getting values RPC interface PUB/SUB interface While a device driver for a device will periodically broadcast the state of a device you may want an up to the moment value for point on a device. As of VOLTTRON 3.5 it is no longer required to have the device scheduled before you can use this interface.

Setting Values RPC interface PUB/SUB interface Failure to schedule the device first will result in an error.

Errors Setting Values If there is an error the RPC interface will raise an exception and the PUB/SUB interface will publish to devices/actuators/error// The headder of the publish will take this form: { 'requesterID': } and a message body in this form: { 'type': 'value': }

224 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Common Error Types LockError Raised when a request is made when we do not have permission to use a de- vice. (Forgot to schedule, preempted and we did not handle the preemption message cor- rectly, ran out of time in time slot, etc...) ValueError Message missing (PUB/SUB only) or is the wrong data type. Most other error types involve problems with communication between the VOLTTRON device drivers and the device itself.

Reverting Values and Devices to a Default State As of VOLTTRON 3.5 device drivers are now required to support reverting to a default state. The exact mechanism used to accomplish this is driver specific. Failure to schedule the device first will result in a LockError. RPC revert value interface PUB/SUB revert value interface RPC revert device interface PUB/SUB revert device interface

Canceling a Task RPC interface PUB/SUB interface Cancelling a Task requires two things: • The original requester of the Task. This is the Agent’s ID. • The name of the Task.

Cancel Task Response Both the RPC and PUB/SUB interface respond to requests with the result in the following format: { 'result':<'SUCCESS','FAILURE'>, 'info':, 'data': {} }

Note: There are some things to be aware of when canceling a schedule: • The requesterID and taskID must match the original values from the original request header. • After a Tasks time has passed there is no need to cancel it. Doing so will result in a “TASK_ID_DOES_NOT_EXIST” error.

If an attempt cancel a schedule fails than the “info” item will have any of the following values: “TASK_ID_DOES_NOT_EXIST” Trying to cancel a Task which does not exist. This error can also occur when trying to cancel a finished Task. “AGENT_ID_TASK_ID_MISMATCH” A different agent ID is being used when trying to cancel a Task.

Preemption Publishes If a Task is preempted it will publish the following to the devices/actuators/schedule/result topic:

10.1. Services 225 VOLTTRON Documentation, Release 3.5RC1

{ 'result':'PREEMPTED', 'info': None, 'data':{ 'agentID':, 'taskID': } }

Along with the following header: { 'type':'CANCEL_SCHEDULE', 'requesterID':, 'taskID': }

Note: Remember that if your “LOW_PREEMPT” Task has already started and is preempted you have a grace period to do any clean up before losing access to the device.

Schedule State Publishes Periodically the ActuatorAgent will publish the state of all currently reserved devices. The first publish for a device will happen exactly when the reserved block of time for a device starts. For each device the ActuatorAgent will publish to an associated topic: devices/actuators/schedule/announce/ With the following header: { 'requesterID':, 'taskID': 'window': }

The frequency of the updates is configurable with the “schedule_publish_interval” setting in the configuration. class actuator.agent.ActuatorAgent(heartbeat_interval=60, schedule_publish_interval=60, schedule_state_file=None, preempt_grace_time=60, driver_vip_identity=’platform.driver’, **kwargs) Bases: volttron.platform.vip.agent.Agent The Actuator Agent regulates control of devices by other agents. Agents request a schedule and then issue commands to the device through this agent. The Actuator Agent also sends out the signal to drivers to trigger a device heartbeat. Parameters • heartbeat_interval (float) – Interval in seonds to send out a heartbeat to devices. • schedule_publish_interval (float) – Interval in seonds to publish the currently active schedules. • schedule_state_file (str) – Name of the file to save the current schedule state to. This file is updated every time a schedule changes. • preempt_grace_time (float) – Time in seconds after a schedule is preemted before it is actually cancelled.

226 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

• driver_vip_identity (str) – VIP identity of the Master Driver Agent. get_point(topic, **kwargs) RPC method Gets up to date value of a specific point on a device. Does not require the device be scheduled. Parameters • topic (str) – The topic of the point to grab in the format / • **kwargs – Any driver specific parameters Returns point value Return type any base python type handle_get(peer, sender, bus, topic, headers, message) Requests up to date value of a point. To request a value publish a message to the following topic: devices/actuators/get// with the fallowing header: { 'requesterID': }

The ActuatorAgent will reply on the value topic for the actuator: devices/actuators/value// with the message set to the value the point. handle_revert_device(peer, sender, bus, topic, headers, message) Revert all the writable values on a device. To revert a device publish a message to the following topic: devices/actuators/revert/device/ with the fallowing header: { 'requesterID': }

The ActuatorAgent will reply on the value topic for the actuator: devices/actuators/reverted/device/ to indicate that a point was reverted. Errors will be published on devices/actuators/error// with the same header as the request. handle_revert_point(peer, sender, bus, topic, headers, message) Revert the value of a point. To revert a value publish a message to the following topic: actuators/revert/point//

10.1. Services 227 VOLTTRON Documentation, Release 3.5RC1

with the fallowing header: { 'requesterID': }

The ActuatorAgent will reply on devices/actuators/reverted/point// This is to indicate that a point was reverted. Errors will be published on devices/actuators/error// with the same header as the request. handle_schedule_request(peer, sender, bus, topic, headers, message) Schedule request pub/sub handler An agent can request a task schedule by publishing to the devices/actuators/schedule/request topic with the following header: { 'type':'NEW_SCHEDULE', 'requesterID':, #The name of the requesting agent. 'taskID':, #The desired task ID for this task. It must be unique among all other scheduled tasks. 'priority':, #The desired task priority, must be 'HIGH', 'LOW', or 'LOW_PREEMPT' }

The message must describe the blocks of time using the format described in Device Schedule. A task may be canceled by publishing to the devices/actuators/schedule/request topic with the following header: { 'type':'CANCEL_SCHEDULE', 'requesterID':, #The name of the requesting agent. 'taskID':, #The task ID for the canceled Task. }

requesterID The name of the requesting agent. taskID The desired task ID for this task. It must be unique among all other scheduled tasks. priority The desired task priority, must be ‘HIGH’, ‘LOW’, or ‘LOW_PREEMPT’

No message is requires to cancel a schedule. handle_set(peer, sender, bus, topic, headers, message) Set the value of a point. To set a value publish a message to the following topic: devices/actuators/set// with the fallowing header: { 'requesterID': }

228 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

The ActuatorAgent will reply on the value topic for the actuator: devices/actuators/value// with the message set to the value the point. Errors will be published on devices/actuators/error// with the same header as the request. request_cancel_schedule(requester_id, task_id) RPC method Requests the cancelation of the specified task id. Parameters • requester_id (str) – Requester name. • task_id (str) – Task name. Returns Request result Return type dict Return Values The return values are described in Cancel Task Response. request_new_schedule(requester_id, task_id, priority, requests) RPC method Requests one or more blocks on time on one or more device. Parameters • requester_id (str) – Requester name. • task_id (str) – Task name. • priority (str) – Priority of the task. Must be either “HIGH”, “LOW”, or “LOW_PREEMPT” • requests – A list of time slot requests in the format described in Device Schedule. Returns Request result Return type dict Return Values The return values are described in New Task Response. revert_device(requester_id, topic, **kwargs) RPC method Reverts all points on a device to a default state. Requires the device be scheduled by the calling agent. Parameters • requester_id (str) – Identifier given when requesting schedule. • topic (str) – The topic of the device to revert • **kwargs – Any driver specific parameters

Warning: Calling without previously scheduling a device and not within the time allotted will raise a LockError

10.1. Services 229 VOLTTRON Documentation, Release 3.5RC1

revert_point(requester_id, topic, **kwargs) RPC method Reverts the value of a specific point on a device to a default state. Requires the device be scheduled by the calling agent. Parameters • requester_id (str) – Identifier given when requesting schedule. • topic (str) – The topic of the point to revert in the format / • **kwargs – Any driver specific parameters

Warning: Calling without previously scheduling a device and not within the time allotted will raise a LockError

set_point(requester_id, topic, value, **kwargs) RPC method Sets the value of a specific point on a device. Requires the device be scheduled by the calling agent. Parameters • requester_id (str) – Identifier given when requesting schedule. • topic (str) – The topic of the point to set in the format / • value (any basic python type) – Value to set point to. • **kwargs – Any driver specific parameters Returns value point was actually set to. Usually invalid values cause an error but some drivers (MODBUS) will return a different value with what the value was actually set to. Return type any base python type

Warning: Calling without previously scheduling a device and not within the time allotted will raise a LockError exception actuator.agent.LockError Bases: exceptions.StandardError Error raised when the user does not have a device scheuled and tries to use methods that require exclusive access. actuator.agent.actuator_agent(config_path, **kwargs) Parses the Actuator Agent configuration and returns an instance of the agent created using that configuation. Parameters config_path (str) – Path to a configuation file. Returns Actuator Agent Return type ActuatorAgent actuator.agent.main() Main method called to start the agent. actuator.scheduler module class actuator.scheduler.DeviceState(agent_id, task_id, time_remaining) Bases: tuple

230 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

agent_id Alias for field number 0 task_id Alias for field number 1 time_remaining Alias for field number 2 class actuator.scheduler.RequestResult(success, data, info_string) Bases: tuple data Alias for field number 1 info_string Alias for field number 2 success Alias for field number 0 class actuator.scheduler.Schedule Bases: object check_availability(time_slot) finished(now) get_conflicts(other) Returns a list of our time_slices that conflict with the other schedule get_current_slot(now) get_next_event_time(now) Run this to know when to the next state change is going to happen with this schedule get_schedule() make_current(now) Should be called before working with a schedule. Updates the state to the schedule to eliminate stuff in the past. prune_to_current(grace_time, now) Use this to prune a schedule due to preemption. schedule_slot(time_slot) exception actuator.scheduler.ScheduleError Bases: exceptions.StandardError class actuator.scheduler.ScheduleManager(grace_time, now=None, state_file_name=None) Bases: object cancel_task(agent_id, task_id, now) get_next_event_time(now) get_schedule_state(now) load_state(now) request_slots(agent_id, id_, requests, priority, now=None) save_state(now)

10.1. Services 231 VOLTTRON Documentation, Release 3.5RC1 class actuator.scheduler.Task(agent_id, priority, requests) Bases: object STATE_FINISHED = ‘FINISHED’ STATE_PREEMTED = ‘PREEMPTED’ STATE_PRE_RUN = ‘PRE_RUN’ STATE_RUNNING = ‘RUNNING’ change_state(new_state) check_can_preempt_other(other) get_conflicts(other) get_current_slots(now) get_next_event_time(now) make_current(now) populate_schedule(requests) preempt(grace_time, now) Return true if there are time slots that have a grace period left class actuator.scheduler.TimeSlice(start=None, end=None) Bases: object contains_include_start(other) Similar to == or “in” but includes time == self.start end start stretch_to_include(time_slice)

Module contents tests package

Submodules tests.actuator-tests module tests.test_actuator_pubsub module

Pytest test cases for testing actuator agent using pubsub calls. Tests 3.0 actuator agent with both 2.0 and 3.0 publish agents tests.test_actuator_pubsub.publish(publish_agent, topic, header, message) Parameters • publish_agent – 3.0 agent to use for publishing • topic – topic to publish to • header – header to publish • message – message to publish

232 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

tests.test_actuator_rpc module

Pytest test cases for testing actuator agent using rpc calls.

tests.test_scheduler module

tests.test_scheduler.test_basic() tests.test_scheduler.test_conflict_override() tests.test_scheduler.test_conflict_override_error() tests.test_scheduler.test_conflict_override_fail_on_running_agent() tests.test_scheduler.test_conflict_override_success_running_agent() tests.test_scheduler.test_conflict_override_success_running_agent2() tests.test_scheduler.test_malformed_bad_device() tests.test_scheduler.test_malformed_schdeule_bad_timestr() tests.test_scheduler.test_malformed_schedule() tests.test_scheduler.test_non_conflict_schedule() tests.test_scheduler.test_schedule_conflict() tests.test_scheduler.test_schedule_self_conflict() tests.test_scheduler.test_touching_requests() tests.test_scheduler.test_two_agents_two_devices() tests.test_scheduler.test_two_devices() tests.test_scheduler.verify_add_task(schedule_manager, agent_id, task_id, requests, prior- ity, now)

Module contents

10.1.2 BACnetProxy bacnet_proxy package

Submodules bacnet_proxy.agent module class bacnet_proxy.agent.BACnetProxyAgent(device_address, max_apdu_len, seg_supported, obj_id, obj_name, ven_id, **kwargs) Bases: volttron.platform.vip.agent.Agent This agent creates a virtual bacnet device that is used by the bacnet driver interface to communicate with devices.

ping_device(target_address, device_id) Ping a device with a whois to potentially setup routing.

10.1. Services 233 VOLTTRON Documentation, Release 3.5RC1

read_properties(target_address, point_map, max_per_request=None) Read a set of points and return the results setup_device(address, max_apdu_len=1024, seg_supported=’segmentedBoth’, obj_id=599, obj_name=’sMap BACnet driver’, ven_id=15) write_property(target_address, value, object_type, instance_number, property_name, prior- ity=None, index=None) Write to a property. class bacnet_proxy.agent.BACnet_application(*args) Bases: bacpypes.app.BIPSimpleApplication, bacpypes.task.RecurringTask confirmation(apdu) get_next_invoke_id(addr) Called to get an unused invoke ID. handle_request(iocb) process_task() submit_request(iocb) class bacnet_proxy.agent.IOCB(request, asynccall)

set(value) set_exception(exception) bacnet_proxy.agent.bacnet_proxy_agent(config_path, **kwargs) bacnet_proxy.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called to start the agent.

Module contents

10.1.3 EmailerAgent

emailer package

Submodules

emailer.agent module

class emailer.agent.EmailerAgent(config_path, **kwargs) Bases: volttron.platform.vip.agent.Agent The EmailerAgent is responsible for sending alert emails. The function of the EmailerAgent is to act as the sender of emails for a volttron instance. This class works in conjunction with a volttron central instance to timely send emails to stakeholders. This class’s other respons- ability is to throttle the sending of emails so that the stakeholder is not bombarded with duplicate alerts. onmessage(peer, sender, bus, topic, headers, message) timestamp()

234 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Module contents

10.1.4 ForwardHistorian

forwarder package

Submodules

forwarder.agent module

forwarder.agent.historian(config_path, **kwargs) forwarder.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the aip.

Module contents

10.1.5 MasterDriverAgent

master_driver package

Subpackages

master_driver.interfaces package

Submodules

master_driver.interfaces.bacnet module class master_driver.interfaces.bacnet.Interface(**kwargs) Bases: master_driver.interfaces.BaseInterface configure(config_dict, registry_config_str) get_point(point_name, get_priority_array=False) parse_config(config_string) ping_target() revert_all(priority=None) Revert entrire device to it’s default state revert_point(point_name, priority=None) Revert point to it’s default state schedule_ping() scrape_all() set_point(point_name, value, priority=None)

10.1. Services 235 VOLTTRON Documentation, Release 3.5RC1

class master_driver.interfaces.bacnet.Register(instance_number, object_type, prop- erty_name, read_only, pointName, units, description=’‘, priority=None, list_index=None) Bases: master_driver.interfaces.BaseRegister

master_driver.interfaces.fakedriver module class master_driver.interfaces.fakedriver.EKGregister(read_only, pointName, units, reg_type, default_value=None, description=’‘) Bases: master_driver.interfaces.BaseRegister value class master_driver.interfaces.fakedriver.FakeRegister(read_only, pointName, units, reg_type, default_value=None, description=’‘) Bases: master_driver.interfaces.BaseRegister class master_driver.interfaces.fakedriver.Interface(**kwargs) Bases: master_driver.interfaces.BasicRevert, master_driver.interfaces.BaseInterface

configure(config_dict, registry_config_str) get_point(point_name) parse_config(config_string) master_driver.interfaces.modbus module class master_driver.interfaces.modbus.Interface(**kwargs) Bases: master_driver.interfaces.BasicRevert, master_driver.interfaces.BaseInterface

build_ranges_map() configure(config_dict, registry_config_str) get_point(point_name) insert_register(register) parse_config(config_string) scrape_bit_registers(client, read_only) scrape_byte_registers(client, read_only) class master_driver.interfaces.modbus.ModbusBitRegister(address, type_string, point- Name, units, read_only, de- scription=’‘, slave_id=0) Bases: master_driver.interfaces.modbus.ModbusRegisterBase get_register_count() get_state(client) parse_value(starting_address, bit_stream) set_state(client, value)

236 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

class master_driver.interfaces.modbus.ModbusByteRegister(address, type_string, point- Name, units, read_only, de- scription=’‘, slave_id=0) Bases: master_driver.interfaces.modbus.ModbusRegisterBase get_register_count() get_state(client) parse_value(starting_address, byte_stream) set_state(client, value) exception master_driver.interfaces.modbus.ModbusInterfaceException(string) Bases: pymodbus.exceptions.ModbusException class master_driver.interfaces.modbus.ModbusRegisterBase(address, register_type, read_only, pointName, units, description=’‘, slave_id=0) Bases: master_driver.interfaces.BaseRegister master_driver.interfaces.modbus.modbus_client(*args, **kwds) master_driver.interfaces.radiothermostat module Copyright (c) 2016, Alliance for Sustainable Energy, LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. class master_driver.interfaces.radiothermostat.Interface(**kwargs) Bases: master_driver.interfaces.BaseInterface configure(config_dict, registry_config_str) Configure the Inteface get_point(point_name) Returns the value of a point on the device parse_config(config_string) Parses the file with point_names and properties, and creates registers

10.1. Services 237 VOLTTRON Documentation, Release 3.5RC1

ping_target(address) Ping target function not implemented for this interface revert_all() Sets all points on the device to their default values revert_point(point_name) sets the value of a point to its default value scrape_all() Scrapes the device for current status of all points set_point(point_name, value) Sets the value of a point o the devcie class master_driver.interfaces.radiothermostat.Register(read_only, pointName, de- vice_point_name, units, de- fault_value) Bases: master_driver.interfaces.BaseRegister Inherits from Volttron Register Class master_driver.interfaces.thermostat_api module Copyright (c) 2016, Alliance for Sustainable Energy, LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. class master_driver.interfaces.thermostat_api.ThermostatInterface(url) Bases: object Base interface to get and set values on the thermostat energy_led(data) Controls energy led, possible values: 0,1,2,4 fmode(data) Sets fan’s mode get_cool_pgm(day=’‘) get cool program for a week or a specific day day = {‘mon’,’tue’,’wed’,’thu’,’fri’,’sat’,’sun’}

238 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

for a specific day, say thursday: t.get_cool_pgm(‘thu’) for a week: t.get_cool_pgm() get_heat_pgm(day=’‘) get heat program for a week or a specific day day = {‘mon’,’tue’,’wed’,’thu’,’fri’,’sat’,’sun’} for a specific day, say thursday: t.get_heat_pgm(‘thu’) for a week: t.get_heat_pgm() hold(data) Sets hold controls mode(data) Sets operating mode model() Returns device model over(data) Sets override controls set_cool_pgm(schedules, day=’‘) set cool program for a week or a specific day day = {‘mon’,’tue’,’wed’,’thu’,’fri’,’sat’,’sun’} for a spefic day, say ‘thu’ t.set_cool_pgm(‘{“360, 80, 480, 80, 1080, 80, 1320 , 80”,’thu’) t.set_cool_pgm(‘{ “1”: [360, 70, 480, 70, 1080, 70, 1320, 70], “0”: [360, 66, 480, 58, 1080, 66, 1320, 58], “3”: [360, 66, 480, 58, 1080, 66, 1320, 58], “2”: [360, 66, 480, 58, 1080, 66, 1320, 58], “5”: [360, 66, 480, 58, 1080, 66, 1320, 58], “4”: [360, 66, 480, 58, 1080, 66, 1320, 58], “6”: [360, 66, 480, 58, 1080, 66, 1320, 58] }’) set_heat_pgm(schedules, day=’‘) set heat program for a week or a specific day day = {‘mon’,’tue’,’wed’,’thu’,’fri’,’sat’,’sun’} for a spefic day, say ‘thu’ t.set_heat_pgm(‘{“360, 80, 480, 80, 1080, 80, 1320 , 80”,’thu’) for a week t.set_heat_pgm(‘{ “1”: [360, 70, 480, 70, 1080, 70, 1320, 70], “0”: [360, 66, 480, 58, 1080, 66, 1320, 58], “3”: [360, 66, 480, 58, 1080, 66, 1320, 58], “2”: [360, 66, 480, 58, 1080, 66, 1320, 58], “5”: [360, 66, 480, 58, 1080, 66, 1320, 58], “4”: [360, 66, 480, 58, 1080, 66, 1320, 58], “6”: [360, 66, 480, 58, 1080, 66, 1320, 58] }’) t_cool(data) Sets cooling setpoint t_heat(data) Sets heating setpoint t_setpoint(data, point, tmode=’‘) Sets cooling setpoint tstat() Returns current deicve paramenters master_driver.interfaces.thermostat_api.Thermostat_API(url) Call the interface

10.1. Services 239 VOLTTRON Documentation, Release 3.5RC1

master_driver.interfaces.universal module

Module contents

Driver Development New drivers are implemented by subclassing BaseInterface. While it is possible to create an Agent which handles communication with a new device it will miss out on the benefits of creating a proper interface for the Master Driver Agent. Creating an Interface for a device allows users of the device to automatically benefit from the following platform features: • Existing Agents can interact with the device via the Actuator Agent without any code changes. • Configuration follows the standard form of other devices. Existing and future tools for configuring de- vices on the platform will work with the new device driver. • Historians will automatically capture data published by the new device driver. • Device data can be graphed in VOLTTRON Central in real time. • If the device can receive a heartbeat signal the driver framework can be configured to automatically send a heartbeat signal. • When the configuration store feature is rolled out the device can by dynamically configured through the platform.

Creating a New Interface To create a new device driver create a new module in the MasterDriverAgent.master_driver.interfaces package. The name of this module will be the name to use in the “driver_type” setting in a driver configuration file in order to load the new driver. In the new module create a subclass of BaseInterface called Interface. The Interface class must implement the following methods: • BaseInterface.configure() • BaseInterface.set_point() • BaseInterface.get_point() • BaseInterface.scrape_all() These methods are required but can be implemented using the BasicRevert mixin. • BaseInterface.revert_point() • BaseInterface.revert_all() Each point on the device must be represented by an instance of the BaseInterface. Create one or more subclasses of BaseInterface as needed to represent the points on a device.

Interface Configuration and Startup When processing a driver configuration file the Master Driver Agent will use the “driver_type” setting to automatically find and load the appropriate Interface class for the desired driver. After loading the class the Master Driver Agent will call BaseInterface.configure() with the contents of the “driver_config” section of the driver configuration file parsed into a python dictionary and the contents of the file referenced in “registry_config” entry.

240 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

BaseInterface.configure() must setup register representations of all points on a device by creating instances of BaseRegister (or a subclass) and adding them to the Interface with BaseInterface.insert_register(). After calling BaseInterface.configure() the Master Driver Agent will use the created registers to create meta data for each point on the device.

Device Scraping The work scheduling and publish periodic device scrapes is handled by the Master Driver Agent. When a scrape starts the Master Driver Agent calls the BaseInterface.scrape_all(). It will take the results of the call and attach meta data and and publish as needed.

Device Interaction Requests to interact with the device via any method supported by the platform are routed to the correct Interface instance by the Master Driver Agent. Most commands originate from RPC calls to the Actuator Agent and are forwarded to the Master Driver Agent. • A command to set the value of a point on a device results in a call to BaseInterface.set_point(). • A request for the current value of a point on a device results in a call to BaseInterface.get_point(). • A request to revert a point on a device to its default state results in a call to BaseInterface.revert_point(). • A request to revert an entire device to its default state results in a call to BaseInterface.revert_all().

Registers The Master Driver Agent uses the BaseInterface.get_register_names() and BaseInterface.get_register_by_name() methods to get registers to setup meta data. This means that its a requirement to use the BaseRegister class to store information about points on a devices.

Using the BasicRevert Mixin If the device protocol has no support for reverting to a default state an Interface this functionality can be implemented with the BasicRevert mixin. When using the BasicRevert mixin you must specify it first in the list of parent classes, otherwise it won’t Python won’t detect that the BaseInterface.revert_point() and BaseInterface.revert_all() have been implemented. If desired the BasicRevert.set_default() can be used by the Interface class to set values for each point to revert to. class master_driver.interfaces.BaseInterface(vip=None, core=None, **kwargs) Bases: object Main class for implementing support for new devices. All interfaces must subclass this. Parameters • vip – A reference to the MasterDriverAgent vip subsystem. • core – A reference to the parent driver agent’s core subsystem. build_register_map() configure(config_dict, registry_config_str) Configures the Interface for the specific instance of a device. Parameters • config_dict (dict) – The “driver_config” section of the driver configuration file.

10.1. Services 241 VOLTTRON Documentation, Release 3.5RC1

• registry_config_str (str) – The contents of the registry configuration file. This method must setup register representations of all points on a device by creat- ing instances of BaseRegister (or a subclass) and adding them to the Interface with BaseInterface.insert_register(). get_point(point_name, **kwargs) Get the current value for the point name given. Parameters • point_name (str) – Name of the point to retrieve. • kwargs – Any interface specific parameters. Returns Point value get_register_by_name(name) Get a register by it’s point name. Parameters name (str) – Point name of register. Returns An instance of BaseRegister Return type BaseRegister get_register_names() Get a list of register names. :return: List of names :rtype: list get_registers_by_type(reg_type, read_only) Get a list of registers by type. Useful for an Interface that needs to categorize registers by type when doing a scrape. Parameters • reg_type (str) – Register type. Either “bit” or “byte”. • read_only (bool) – Specify if the desired registers are read only. Returns An list of BaseRegister instances. Return type list insert_register(register) Inserts a register into the Interface. Parameters register (BaseRegister) – Register to add to the interface. revert_all(**kwargs) Revert entire device to it’s default state Parameters kwargs – Any interface specific parameters. revert_point(point_name, **kwargs) Revert point to it’s default state. Parameters kwargs – Any interface specific parameters. scrape_all() Method the Master Driver Agent calls to get the current state of a device for publication. Returns Point names to values for device. Return type dict

242 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

set_point(point_name, value, **kwargs) Set the current value for the point name given. Implementations of this method should make a reasonable effort to return the actual value the point was set to. Some protocols/devices make this difficult. (I’m looking at you BACnet) In these cases it is acceptable to return the value that was requested if no error occurs. Parameters • point_name (str) – Name of the point to retrieve. • value – Value to set the point to. • kwargs – Any interface specific parameters. Returns Actual point value set. class master_driver.interfaces.BaseRegister(register_type, read_only, pointName, units, de- scription=’‘) Bases: object Class for containing information about a point on a device. Should be extended to support the device protocol to be supported. The member variable python_type should be overridden with the equivalent python type object. Defaults to int. This is used to generate meta data. Parameters • register_type (str) – Type of the register. Either “bit” or “byte”. Usually “byte”. • read_only (bool) – Specify if the point can be written to. • pointName (str) – Name of the register. • units (str) – Units of the value of the register. • description (str) – Description of the register. The Master Driver Agent will use BaseRegister.get_units() to populate metadata for publishing. When instantiating register instances be sure to provide a useful string for the units argument. get_description() Returns Register description Return type str get_register_python_type() Returns The python type of the register. Return type type get_register_type() Returns (register_type, read_only) Return type tuple get_units() Returns Register units Return type str

10.1. Services 243 VOLTTRON Documentation, Release 3.5RC1 class master_driver.interfaces.BasicRevert(**kwargs) Bases: object A mixin that implements the BaseInterface.revert_all() and BaseInterface.revert_point() methods on an Interface. It works by tracking change to all writable points until a set_point call is made. When this happens the point is marked dirty and the previous value is remembered. When a point is reverted via either a revert_all or revert_point call the dirty values are set back to the clean value using the BasicRevert._set_point() method. As it must hook into the setting and scraping of points it implements the BaseInterface.scrape_all() and BaseInterface.set_point() methods. It then adds BasicRevert._set_point() and BasicRevert._scrape_all() to the abstract interface. An existing interface that wants to use this class can simply mix it in and rename it’s set_point and scrape_all methods to _set_point and _scrape_all respec- tively. An BaseInterface may also override the detected clean value with its own value to revert to by call- ing BasicRevert.set_default(). While default values can be set anytime they should be set in the BaseInterface.configure() call. revert_all(**kwargs) Implementation of BaseInterface.revert_all() Calls BasicRevert._set_point() with point_name and the value to revert the point to for every writable point on a device. Currently **kwargs is ignored. revert_point(point_name, **kwargs) Implementation of BaseInterface.revert_point() Revert point to its default state. Calls BasicRevert._set_point() with point_name and the value to revert the point to. Parameters point_name (str) – Name of the point to revert. Currently **kwargs is ignored. scrape_all() Implementation of BaseInterface.scrape_all() set_default(point, value) Set the value to revert a point to. Parameters • point (str) – name of point to set. • value – value to set the point to. set_point(point_name, value) Implementation of BaseInterface.set_point() Passes arguments through to BasicRevert._set_point() exception master_driver.interfaces.DriverInterfaceError Bases: exceptions.Exception class master_driver.interfaces.RevertTracker Bases: object A helper class for tracking the state of writable points on a device.

244 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

clear_dirty_point(point) Clears the dirty flag on a point. Parameters point (str) – Name of dirty point flag to clear. get_all_revert_values() Returns a dict of points to revert values. If no default is set use the clean value, otherwise returns the default value. If no default value is set and a no clean values have been submitted a point value will be an instance of DriverInterfaceError. Parameters point (str) – Name of point to get. Returns Values to revert to. Return type dict get_revert_value(point) Returns the clean value for a point if no default is set, otherwise returns the default value. If no default value is set and a no clean values have been submitted raises DriverInterfaceError. Parameters point (str) – Name of point to get. Returns Value to revert to. mark_dirty_point(point) Sets the dirty flag on a point. Ignores points with a default value. Parameters point (str) – Name of point flag to dirty. set_default(point, value) Set the value to revert a point to. Overrides any clean value detected. Parameters • point (str) – name of point to set. • value – value to set the point to. update_clean_values(points) Update all state of all the clean point values for a device. If a point is marked dirty it will not be updated. Parameters points (dict) – dict of point names to values. master_driver.tests package

Submodules master_driver.tests.test_revert_mixin module

Module contents

10.1. Services 245 VOLTTRON Documentation, Release 3.5RC1

Submodules

master_driver.agent module

class master_driver.agent.MasterDriverAgent(driver_config_list, scalability_test=False, scala- bility_test_iterations=3, staggered_start=None, **kwargs) Bases: volttron.platform.vip.agent.Agent device_startup_callback(topic, driver) get_point(path, point_name, **kwargs) heart_beat() revert_device(path, **kwargs) revert_point(path, point_name, **kwargs) scrape_ending(topic) scrape_starting(topic) set_point(path, point_name, value, **kwargs) starting(sender, **kwargs) master_driver.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called to start the agent. master_driver.agent.master_driver_agent(config_path, **kwargs) master_driver.driver module class master_driver.driver.DriverAgent(parent, config_name, **kwargs) Bases: volttron.platform.vip.agent.BasicAgent get_config(config_name) get_interface(driver_type, config_dict, config_string) Returns an instance of the interface get_paths_for_point(point) get_point(point_name, **kwargs) heart_beat() periodic_read() revert_all(**kwargs) revert_point(point_name, **kwargs) set_point(point_name, value, **kwargs) setup_device() starting(sender, **kwargs)

246 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

master_driver.driver_exceptions module

exception master_driver.driver_exceptions.DriverConfigError Bases: master_driver.driver_exceptions.DriverError exception master_driver.driver_exceptions.DriverError Bases: exceptions.StandardError

master_driver.driver_locks module

master_driver.driver_locks.configure_publish_lock(max_connections=0) master_driver.driver_locks.configure_socket_lock(max_connections=0) master_driver.driver_locks.publish_lock(*args, **kwds) master_driver.driver_locks.socket_lock(*args, **kwds)

Module contents

10.1.6 MongodbHistorian

mongodb package

Submodules

mongodb.historian module

mongodb.historian.historian(config_path, **kwargs) mongodb.historian.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. @param argv:

Module contents

10.1.7 MultiBuilding multibuilding package

Submodules multibuilding.agent module

VOLTTRON platform™ service for multi-building messaging. multibuilding.agent.MultiBuildingAgent(config_path=None, **kwargs) Return agent object providing multi-building messaging. The configuration file, if given by config_path, may contain the declarations below. An initial configuration may be passed in as a dictionary via the config keyword argument.

10.1. Services 247 VOLTTRON Documentation, Release 3.5RC1

building-publish-address: A ØMQ address used to publish to the building’s message bus. Defaults to ‘tcp://0.0.0.0:9161‘. building-subscribe-address: A ØMQ address used to subscribe to the building’s message bus. De- faults to ‘tcp://0.0.0.0:9160‘. public-key, secret-key: Curve keypair (create with zmq.curve_keypair()) to use for authentication and encryption. If not provided, all communications will be unencrypted. cleanup-period: Frequency, in seconds, to check for and close stale connections. Defaults to 600 seconds (10 minutes). hosts: A mapping (dictionary) of building names to publish/subscribe addresses. Each entry is of the form: : {‘pub’: , ‘sub’: , ‘public- key’: , ‘allow’: } where , , , and are all strings specifying the building name as ‘CAMPUS/BUILDING’, the publish and subscribe addresses as ØMQ addresses, the curve public key, and either ‘pub’ or ‘sub’ to allow publish only or both publish and subscribe. uuid: A UUID to use in the Cookie header. If not given, one will be automatically generated. multibuilding.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

Module contents

10.1.8 OpenEISHistorian openeis package

Submodules openeis.historian module openeis.historian.historian(config_path, **kwargs) openeis.historian.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

248 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

openeis.settings module

Module contents

10.1.9 SQLHistorian

sqlhistorian package

Subpackages

sqlhistorian.db package

Submodules

sqlhistorian.db.basedb module class sqlhistorian.db.basedb.DbDriver(dbapimodule, **kwargs) Bases: object commit() get_topic_map() insert_data(ts, topic_id, data) insert_data_query() insert_meta(topic_id, metadata) insert_meta_query() insert_topic(topic) insert_topic_query() query(topic, start=None, end=None, skip=0, count=None, order=’FIRST_TO_LAST’) This function should return the results of a query in the form: {“values”: [(timestamp1, value1), (times- tamp2, value2), ...], “metadata”: {“key1”: value1, “key2”: value2, ...}} metadata is not required (The caller will normalize this to {} for you) rollback() select(query, args) update_topic(topic, topic_id) sqlhistorian.db.mysqlfuncts module class sqlhistorian.db.mysqlfuncts.MySqlFuncts(connect_params, tables_def ) Bases: sqlhistorian.db.basedb.DbDriver get_topic_map() init_microsecond_support() insert_data_query() insert_meta_query()

10.1. Services 249 VOLTTRON Documentation, Release 3.5RC1

insert_topic_query() query(topic_id, start=None, end=None, skip=0, count=None, order=’FIRST_TO_LAST’) This function should return the results of a query in the form: {“values”: [(timestamp1, value1), (times- tamp2, value2), ...], “metadata”: {“key1”: value1, “key2”: value2, ...}} metadata is not required (The caller will normalize this to {} for you) update_topic_query() sqlhistorian.db.sqlitefuncts module class sqlhistorian.db.sqlitefuncts.SqlLiteFuncts(connect_params, tables_def ) Bases: sqlhistorian.db.basedb.DbDriver get_topic_map() insert_data_query() insert_meta_query() insert_topic_query() query(topic_id, start=None, end=None, skip=0, count=None, order=’FIRST_TO_LAST’) This function should return the results of a query in the form: {“values”: [(timestamp1, value1), (times- tamp2, value2), ...], “metadata”: {“key1”: value1, “key2”: value2, ...}} metadata is not required (The caller will normalize this to {} for you) @param order: @param count: @param skip: @param end: @param start: @param topic_id: update_topic_query()

Module contents

Submodules sqlhistorian.historian module sqlhistorian.historian.historian(config_path, **kwargs) sqlhistorian.historian.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main entry point for the agent. Parameters argv – Returns

250 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

sqlhistorian.settings module

Module contents

10.1.10 SysMonAgent sysmon package

Submodules sysmon.agent module class sysmon.agent.SysMonAgent(config, **kwargs) Bases: volttron.platform.vip.agent.Agent Monitor utilization of system resources (CPU, memory, disk) The percent usage of each system resource can be queried via RPC and they are published periodically to configured topics. Parameters config (dict) – Configuration dict Example configuration: { "base_topic":"datalogger/log/platform", "cpu_check_interval":5, "memory_check_interval":5, "disk_check_interval":5, "disk_path":"/" }

cpu_percent() Return CPU usage percentage disk_percent() Return usage of disk mounted at configured path memory_percent() Return memory usage percentage reconfigure(**kwargs) Reconfigure the agent start(sender, **kwargs) Set up periodic publishing of system resource data sysmon.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the platform. sysmon.agent.sysmon_agent(config_path, **kwargs) Load the SysMon Agent configuration and returns and instance of the agent created using that configuration. Parameters config_path (str) – Path to a configuration file. Returns SysMonAgent instance Return type SysMonAgent

10.1. Services 251 VOLTTRON Documentation, Release 3.5RC1

Module contents

10.1.11 ThresholdDetectionAgent thresholddetection package

Submodules thresholddetection.agent module class thresholddetection.agent.ThresholdDetectionAgent(config, **kwargs) Bases: volttron.platform.vip.agent.Agent Listen to topics and publish alerts when thresholds are passed. The agent’s configuration specifies which topics to watch, the topic’s threshold, and the message to send in an alert. Topics in the watch_max list trigger alerts when the published data are greater than the specified threshold. Topics in the watch_min list trigger alerts when the published data are less than the specified threshold. Non- numberic data will be ignored. Alerts are published to alert/TOPIC where TOPIC is the watched topic. Example configuration: { "watch_max":[ { "topic":"datalogger/log/platform/cpu_percent", "threshold": 99, "message":"CPU ({topic}) exceeded {threshold} percent", "enabled": true } ] "watch_min":[ { "topic":"some/temperature/topic", "threshold":0, "message":"Temperature is below {threshold}", "enabled": true } ] }

alert(message, topic) Raise alert for the given topic Parameters • message (str) – Message to include in alert • topic (str) – PUB/SUB topic that caused alert start(sender, **kwargs) thresholddetection.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the platform. thresholddetection.agent.thresholddetection_agent(config_path, **kwargs) Load configuration for ThresholdDetectionAgent

252 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Parameters config_path (str) – Path to a configuration file. Returns ThresholdDetectionAgent instance Return type ThresholdDetectionAgent

Module contents

10.1.12 TwistdLauncher launcher package

Submodules launcher.agent module class launcher.agent.ProcessAgent(subscribe_address, process, **kwargs) Bases: volttron.platform.agent.base.BaseAgent finish() poll_process() setup() launcher.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. launcher.settings module

Module contents

10.1.13 VolttronCentral rpc_test_client module rpc_test_client.do_rpc(method, params=None) rpc_test_client.exec_method(platform_uuid, agent_uuid, method, params) rpc_test_client.get_dict(text) rpc_test_client.inspect_agent(platform_uuid, agent_uuid) rpc_test_client.inspect_method(platform_uuid, agent_uuid, method) rpc_test_client.list_agents(platform_uuid) rpc_test_client.register_instance(discovery_address) rpc_test_client.register_platform(address, identity)

10.1. Services 253 VOLTTRON Documentation, Release 3.5RC1

volttroncentral package

Submodules

volttroncentral.agent module

The VolttronCentral(VCA) agent is used to manage remote VOLTTRON instances. The VCA exposes a JSON-RPC based web api and a web enabled visualization framework. The web enabled framework is known as VOLTTRON Central Management Console (VCMC). In order for an instance to be able to be managed by VCMC a vcplatform.agent.VolttronCentralPlatform must be executing on the instance. If there is a vcplatform.agent.VolttronCentralPlatform running on the same instance as VCA it will be automatically registered as a managed instance. Otherwise, there are two different paths to registering an instance with VCA. 1. Through the web api a call to the JSON-RPC method register_instance. 2. From an external platform through pub/sub. this secondary method is preferred when deploying instances in the field that need to “phone home” to VCA after being deployed. class volttroncentral.agent.Connected Bases: object agent is_connected() class volttroncentral.agent.ConnectedLocalPlatform(agent) Bases: volttroncentral.agent.Connected agent disconnect() is_connected() class volttroncentral.agent.ConnectedPlatform(address, serverkey, publickey, secretkey) Bases: volttroncentral.agent.Connected agent connect() disconnect() is_connected() class volttroncentral.agent.VolttronCentralAgent(config_path, **kwargs) Bases: volttron.platform.vip.agent.Agent Agent for managing many volttron instances from a central web ui. During the get_platform(platform_uuid) get_platforms() Retrieves the platforms that have been registered with VC. @return: jsonrpc(env, data) The main entry point for ^jsonrpc data

254 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

This method will only accept rpcdata. The first time this method is called, per session, it must be using get_authorization. That will return a session token that must be included in every subsequent request. The session is tied to the ip address of the caller. Parameters • env (object) – Environment dictionary for the request. • data (object) – The JSON-RPC 2.0 method to call. Return object An JSON-RPC 2.0 response. list_platform_details() register_instance(discovery_address) Adds discovery_address to proposed list of agents. This method is called from a configured agent to hone into the VolttronCentralAgent. An administrator must then choose to accept the call from this agent before the agent will be granted status. Parameters string – The url of the discovery_address for the platform. unregister_platform(platform_uuid) volttroncentral.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. :param argv: :return:

volttroncentral.authenticate module

A simple authorization for authenticating users with known credentials. @author: Craig Allwardt class volttroncentral.authenticate.Authenticate(user_map)

authenticate(username, password) Authenticate that the user is known to the system. Return groups of the user if the user is known otherwise returns None. :param username: :param password: :return: list(groups) or None volttroncentral.registry module exception volttroncentral.registry.DuplicateUUIDError Bases: exceptions.Exception class volttroncentral.registry.PlatformRegistry(retrieve_fn, store_fn) Bases: object Container class holding registered platforms and services. The goal of this class is to allow a single point of knowlege of all of the platforms and services registered with the volttron central agent. add_update_tag(platform_uuid, key, value) Add a tag to the specified platform’s entry. Parameters

10.1. Services 255 VOLTTRON Documentation, Release 3.5RC1

• platform_uuid – • key – • value – Returns static build_entry(vip_address, serverkey, discovery_address, display_name=None, is_local=False, vcp_publickey=None) get_agent_list(platform_uuid) get_devices(platform_uuid) get_performance(platform_uuid) get_platform(platform_uuid) Returns a platform associated with a specific uuid instance. get_platform_by_address(vip_address) get_platforms() Returns all of the registerd platforms dictionaries. get_tag(platform_uuid, key) get_vip_addresses() Return all of the different vip addresses available. Returns package() register(entry) Registers a PlatformEntry with the registry. :param entry: :return: unpackage(data) unregister(vip_address) update_agent_list(platform_uuid, agent_list) Update the agent list node for the platform uuid that is passed. update_devices(platform_uuid, devices) update_performance(platform_uuid, performance) class volttroncentral.registry.RegistryEntry(vip_address, serverkey, discovery_address, is_local, platform_uuid, display_name, tags, vcp_publickey) Bases: tuple discovery_address Alias for field number 2 display_name Alias for field number 5 is_local Alias for field number 3 platform_uuid Alias for field number 4 serverkey Alias for field number 1

256 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

tags Alias for field number 6 vcp_publickey Alias for field number 7 vip_address Alias for field number 0

volttroncentral.resource_directory module

class volttroncentral.resource_directory.ResourceDirectory

platform_registry

volttroncentral.sessions module

class volttroncentral.sessions.SessionHandler(authenticator) A handler for dealing with authentication of sessions The SessionHandler requires an authenticator to be handed in to this object in order to authenticate user. The authenticator must implement an interface that expects a method called authenticate with parameters username and password. The return value must be either a list of groups the user belongs two or None. If successful then the a session token is generated and added to a cache of validated users to be able to be checked against. The user’s ip address is stored with the token for further checking of authentication. authenticate(username, password, ip) Authenticates a user with the authenticator. This is the main login function for the system. check_session(token, ip) Check if a user token has been authenticated. @return: A users session information or False.

Module contents

10.1.14 VolttronCentralPlatform vcplatform package

Submodules vcplatform.agent module exception vcplatform.agent.AlreadyManagedError Bases: exceptions.StandardError Raised when a different volttron central tries to register.n

10.1. Services 257 VOLTTRON Documentation, Release 3.5RC1 exception vcplatform.agent.CannotConnectError Bases: exceptions.StandardError Raised if the connection parameters are not set or invalid class vcplatform.agent.VolttronCentralPlatform(config_path, **kwargs) Bases: volttron.platform.vip.agent.Agent agent_status(agent_uuid) get_devices() get_health() get_publickey() get_setting(key) is_managed() list_agent_methods(method, params, id, agent_uuid) list_agents() List the agents that are installed on the platform. Note this does not take into account agents that are connected with the instance, but only the ones that are installed and have a uuid. Returns A list of agents. manage(address, vcserverkey, vcpublickey) Allows the VolttronCentralPlatform to be managed. From the web perspective this should be after the user has specified that a user has blessed an agent to be able to be managed. When the user enters a discovery address in VolttronCentral it is implied that the user wants to manage a platform. :returns publickey of the VolttronCentralPlatform on_heartbeat_topic(peer, sender, bus, topic, headers, message) publish_to_peers(topic, message, headers=None) reconfigure(**kwargs) register_service(vip_identity) restart_agent(agent_uuid) route_request(id, method, params) set_setting(key, value) start_agent(agent_uuid) status_agents() stop_agent(agent_uuid) stoping(sender, **kwargs) update_sibling_address_cache() vcplatform.agent.find_registration_address(vip_addresses) vcplatform.agent.is_ip_private(vip_address)

258 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

vcplatform.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. :param argv: :return:

Module contents

10.1.15 WeatherAgent

weather package

Submodules

weather.settings module

weather.weatheragent module

Module contents

weathertest module

weathertest.onmessage(peer, sender, bus, topic, headers, message)

10.1.16 sMAPHistorian

smaphistorian package

Submodules

smaphistorian.historian module

smaphistorian.historian.SMAPHistorianAgent(config_path, **kwargs) There is a potential for conflict if multiple historians are writing to the same sMAP Archiver. If historians create paths in the same parent they could create duplicates. This agent maintains a local understanding of topic in sMAP and will not know that another historian added a topic. If another historian creates: “cam- pus/building/device/point1” that point will not be in the local dictionary and this agent will create it with another uuid. smaphistorian.historian.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

10.1. Services 259 VOLTTRON Documentation, Release 3.5RC1

smaphistorian.settings module

Module contents

10.2 Applications

10.2.1 AFDDAgent afdd package

Submodules afdd.afdd module class afdd.afdd.AFDD(agent, config_path) Doc for AFDD abs_diff_mat_dat(num_minutes, flag) Calculate the average difference between the mixed-air temperature reading and the discharge- air temper- ature reading afdd0() Air-side diagnostic to check for cosistency between the mixed-air temperature sensor and discharge-air temperature sensor when the unit is not cooling or heating afdd1() Air-side diagnostic to check if the outdoor-air damper can modulate from 0% to 100% afdd2() Air-side diagnostic to check for air temperature sensor problems afdd3() Air-side diagnostic to check if RTU is economizing when it should afdd4() Air-side diagnostic to check if RTU is economizing when it should not afdd5() Air-side diagnostic to determine if the RTU is supplying excess air when the damper should be at the minimum position for ventilation afdd6() Air-side diagnostic to determine if the RTU is providing sufficient air for ventilation clean_up() command_damper(command) Command outdoor air damper to a new position command_error_handler(error_type) Handle actuator error for attempted set of RTU actuation point command_outdoor_air_temperature_bias(value) Command outdoor-air temperature bias

260 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

command_volttron_flag(value) Command VOLTTRON Flag get_rtu_status() Check heating and cooling status. If the unit heating or cooling, turn it off init_algo_state() oad_modulation_check(num_minutes, temp1_name, temp2_name) Check if the outdoor-air damper appears to be modulating publish_to_smap(smap_identifier, afdd_msg, smap_energyid, energy_impact) Push diagnostic results and energy impact to sMAP historian run() run_all() Check pre-requisites and run diagnostic functions sequentially sensor_error_check(mat_name) Check for problems with air-temperature sensors set_points_name(config_path) afdd.afddagent module afdd.afddagent.AFDDAgent(config_path, **kwargs) afdd.afddagent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

Module contents

10.2.2 AirsideRCxAgent airside package

Subpackages airside.diagnostics package

Submodules airside.diagnostics.common module Copyright (c) 2016, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution.

10.2. Applications 261 VOLTTRON Documentation, Release 3.5RC1

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, r favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 airside.diagnostics.common.check_date(current_time, timestamp_array) Check current timestamp with previous timestamp to verify that there are no large missing data gaps. airside.diagnostics.common.check_run_status(timestamp_array, current_time, no_required_data) airside.diagnostics.common.setpoint_control_check(setpoint_array, point_array, allow- able_deviation, dx_name, dx_tag, token, token_offset) Verify that point is tracking well with set point. ARGS: setpoint_array (list(floats): airside.diagnostics.common.validation_builder(validate, dx_name, data_tag) airside.diagnostics.reset_sched_rcx module Copyright (c) 2016, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT

262 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, r favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 class airside.diagnostics.reset_sched_rcx.SchedResetRcx(unocc_time_threshold, un- occ_stp_threshold, mon- day_sch, tuesday_sch, wednesday_sch, thursday_sch, friday_sch, saturday_sch, sunday_sch, no_req_data, stpr_reset_threshold, sat_reset_threshold, anal- ysis) Bases: object Schedule, supply-air temperature, and duct static pressure auto-detect diagnostics for AHUs or RTUs. no_sat_stpt_reset(dx_result) Auto-RCx to detect whether a supply-air temperature set point reset is implemented. no_static_pr_reset(dx_result) Auto-RCx to detect whether a static pressure set point reset is implemented. reinitialize(start_new_analysis_time, start_new_analysis_sat_stpt, start_new_analysis_stcpr_stpt, stcpr_data, fan_status) Reinitialize data arrays sched_rcx_alg(current_time, stcpr_data, stcpr_stpt_data, sat_stpt_data, fan_stat_data, dx_result, sched_val) Check schedule status and unit operational status. unocc_fan_operation(dx_result) If the AHU/RTU is operating during unoccupied periods inform the building operator. airside.diagnostics.reset_sched_rcx.create_table_key(table_name, timestamp)

10.2. Applications 263 VOLTTRON Documentation, Release 3.5RC1 airside.diagnostics.satemp_rcx module Copyright (c) 2016, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, r favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 class airside.diagnostics.satemp_rcx.SupplyTempRcx(no_req_data, auto_correct_flag, stpt_allowable_dev, rht_on_thr, high_dmpr_thr, percent_dmpr_thr, percent_rht_thr, min_sat_stpt, sat_retuning, rht_valve_thr, max_sat_stpt, analysis, sat_stpt_cname) Bases: object Air-side HVAC Self-Correcting Diagnostic: Detect and correct supply-air temperature problems. Args: timestamp_arr (List[datetime]): timestamps for analysis period. sat_stpt_arr (List[float]): supply-air temperature set point for analysis period. satemp_arr (List[float]): supply-air temperature for analysis period. rht_arr (List[float]): terminal box reheat command for analysis period. high_sat(dx_result, avg_sat_stpt) Diagnostic to identify and correct high supply-air temperature

264 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

(correction by modifying SAT set point) low_sat(dx_result, avg_sat_stpt) Diagnostic to identify and correct low supply-air temperature (correction by modifying SAT set point) reinitialize() Reinitialize data arrays. sat_rcx(current_time, sat_data, sat_stpt_data, zone_rht_data, zone_dmpr_data, dx_result, validate) Manages supply-air diagnostic data sets. Args: current_time (datetime): current timestamp for trend data. sat_data (lst of floats): supply-air tem- perature measurement for AHU.

sat_stpt_data (List[floats]): supply-air temperature set point data for AHU. zone_rht_data (List[floats]): reheat command for terminal boxes served by AHU. zone_dmpr_data (List[floats]): damper command for terminal boxes served by AHU.

dx_result (Object): Object for interacting with platform and devices. Returns: Results object (dx_result) to Application. airside.diagnostics.satemp_rcx.create_table_key(table_name, timestamp) airside.diagnostics.stcpr_rcx module Copyright (c) 2016, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights.

10.2. Applications 265 VOLTTRON Documentation, Release 3.5RC1

Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, r favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 class airside.diagnostics.stcpr_rcx.DuctStaticRcx(no_req_data, auto_correct_flag, stpt_allowable_dev, max_stcpr_stpt, stcpr_retuning, zone_high_dmpr_threshold, zone_low_dmpr_threshold, hdzn_dmpr_thr, min_stcpr_stpt, analysis, stcpr_stpt_cname) Bases: object Air-side HVAC Self-Correcting Diagnostic: Detect and correct duct static pressure problems. duct_static(current_time, stcpr_stpt_data, stcpr_data, zn_dmpr_data, low_dx_cond, high_dx_cond, dx_result, validate) Check duct static pressure RCx pre-requisites and assemble the duct static pressure analysis data set. high_stcpr_dx(dx_result, avg_stcpr_stpt) Diagnostic to identify and correct high duct static pressure (correction by modifying duct static pressure set point) low_stcpr_dx(dx_result, avg_stcpr_stpt) Diagnostic to identify and correct low duct static pressure (correction by modifying duct static pressure set point). reinitialize() Reinitialize data arrays airside.diagnostics.stcpr_rcx.create_table_key(table_name, timestamp)

Module contents

Submodules airside.airside_retuning_rcx module

Copyright (c) 2016, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT

266 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, r favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 class airside.airside_retuning_rcx.Application(no_required_data=10, warm_up_time=15, duct_stcpr_retuning=0.15, max_duct_stcpr_stpt=2.5, high_sf_threshold=100.0, zone_high_damper_threshold=90.0, zone_low_damper_threshold=10.0, min_duct_stcpr_stpt=0.5, hd- zone_damper_threshold=30.0, low_sf_threshold=20.0, set- point_allowable_deviation=10.0, stcpr_reset_threshold=0.25, per- cent_reheat_threshold=25.0, rht_on_threshold=10.0, sat_reset_threshold=5.0, sat_high_damper_threshold=80.0, percent_damper_threshold=50.0, min_sat_stpt=50.0, sat_retuning=1.0, reheat_valve_threshold=50.0, max_sat_stpt=75.0, un- occ_time_threshold=30.0, un- occ_stp_threshold=0.2, mon- day_sch=[‘5:30’, ‘18:30’], tues- day_sch=[‘5:30’, ‘18:30’], wednes- day_sch=[‘5:30’, ‘18:30’], thurs- day_sch=[‘5:30’, ‘18:30’], fri- day_sch=[‘5:30’, ‘18:30’], sat- urday_sch=[‘0:00’, ‘0:00’], sunday_sch=[‘0:00’, ‘0:00’], auto_correct_flag=False, analy- sis_name=’‘, **kwargs) Bases: volttron.platform.agent.driven.AbstractDrivenAgent

10.2. Applications 267 VOLTTRON Documentation, Release 3.5RC1

Air-side HVAC Auto-Retuning Diagnostics for AHUs. Note: All configurable threshold have default threshold that work well with most equipment/configurations. Args: no_required_data (int): minimum number of measurements required for conclusive analysis. warm_up_time (int): Number of minutes after equipment startup prior to beginning data collection for analysis. duct_stcpr_retuning (float): Amount to increment or decrement the duct static pressure set point high/low duct static pressure set point problem is detected (assumed to be in inches water column (gauge)). max_duct_stcpr_stpt (float): Maximum value for the duct static pressure set point when applying auto-correction. high_sf_threshold (float): Auto-correction for low duct static pressure set point will not be effective if the supply fan for the AHU is operating at or near 100% of full speed. Auto-correction will not be applied if this condition exists. zone_high_damper_threshold (float): zone_low_damper_threshold (float): min_duct_stcpr_stpt (float): Minimum value for the duct static pressure set point when applying auto-correction.

low_sf_threshold (float): Auto-correction for high duct static pressure set point will not be effective if the supply fan for the AHU is operating at or near its minimum SupplyFanSpeed. Auto-correction will not be applied if this condition exists. If the SupplyFanStatus is not available, the supply fan speed can be used to determine if the supply fan is operating. The supply fan will be considered ON when operating at speeds above the minimum SupplyFanSpeed. setpoint_allowable_deviation (float): Maximum acceptable deviation set point for the supply-air temperature and the duct static pressure (averaged over an analysis period, typically one hour).

stcpr_reset_threshold (float): percent_reheat_threshold (float): rht_on_threshold (float): sat_reset_threshold (float): sat_high_damper_threshold (float): percent_damper_threshold (float): min_sat_stpt (float): sat_retuning (float): reheat_valve_threshold (float): max_sat_stpt (float): run(cur_time, points) airside.drivenagent module airside.drivenagent.driven_agent(config_path, **kwargs) Reads agent configuration and converts it to run driven agent. :param kwargs: Any driver specific parameters airside.drivenagent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method.

268 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Module contents

10.2.3 BaselineAgent baseline package

Submodules baseline.agent module class baseline.agent.BaselineAgent(config_path, **kwargs) Bases: volttron.platform.agent.base.PublishMixin, volttron.platform.agent.base.BaseAgent listens for and fulfills requests for generated baselines on_request(topic, headers, message, match) process request for baseline process_request(arg_set) setup() baseline.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

Module contents

10.2.4 CumulativeSumAgent cumulativesum package

Submodules cumulativesum.agent module class cumulativesum.agent.CumulativeSumAgent(config_path, **kwargs) Bases: volttron.platform.agent.base.PublishMixin, volttron.platform.agent.base.BaseAgent listens for and fulfills requests for generated cumulative sums on_request(topic, headers, message, match) process request for cumulative sums process_request(arg_set) setup() cumulativesum.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

10.2. Applications 269 VOLTTRON Documentation, Release 3.5RC1

Module contents

10.2.5 DemandResponseAgent

DemandResponse package

Submodules

DemandResponse.dragent module

DemandResponse.dragent.DemandResponseAgent(config_path, **kwargs) DR application for time of use pricing DemandResponse.dragent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

Module contents

10.2.6 DrivenMatlabAgent

drivenmatlab package

Submodules

drivenmatlab.drivenagent module

drivenmatlab.drivenagent.driven_agent(config_path, **kwargs) Reads agent configuration and converts it to run driven agent. :param kwargs: Any driver specific parameters drivenmatlab.drivenagent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method.

drivenmatlab.drivenagent_pubsub module

drivenmatlab.drivenagent_pubsub.driven_agent(config_path, **kwargs) Driven harness for deployment of OpenEIS applications in VOLTTRON. drivenmatlab.drivenagent_pubsub.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘lan- guage=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method. drivenmatlab.matlab module

Copyright (c) 2014, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

270 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, r favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 class drivenmatlab.matlab.Application(**kwargs) Bases: volttron.platform.agent.driven.AbstractDrivenAgent run(cur_time, points) Sends device points to Matlab application and waits for response. Creates and returns Results object from received response from Matlab application. :param cur_time: timestamp :param points: device point name and value :type cur_time: datetime.datetime :type points: dict :Returns Results object containing commands for devices, log messages and table data.

Rtype results Results object volttron.platform.agent.driven

10.2. Applications 271 VOLTTRON Documentation, Release 3.5RC1

Module contents

10.2.7 EconomizerRCxAgent

economizer package

Submodules

economizer.copy_of_drivenagent module

economizer.copy_of_drivenagent.DrivenAgent(config_path, **kwargs) economizer.copy_of_drivenagent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

economizer.drivenagent module

economizer.drivenagent.DrivenAgent(config_path, **kwargs) Driven harness for deployment of OpenEIS applications in VOLTTRON. economizer.drivenagent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method.

economizer.economizer_rcx module

Copyright (c) 2014, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project.

272 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 class economizer.economizer_rcx.Application(economizer_type=’DDB’, econ_hl_temp=65.0, device_type=’AHU’, temp_deadband=1.0, data_window=30, no_required_data=20, open_damper_time=5, low_supply_fan_threshold=20.0, mat_low_threshold=50.0, mat_high_threshold=90.0, oat_low_threshold=30.0, oat_high_threshold=100.0, rat_low_threshold=50.0, rat_high_threshold=90.0, temp_difference_threshold=4.0, oat_mat_check=5.0, open_damper_threshold=90.0, oaf_economizing_threshold=25.0, oaf_temperature_threshold=4.0, cool- ing_enabled_threshold=5.0, min- imum_damper_setpoint=15.0, ex- cess_damper_threshold=20.0, ex- cess_oaf_threshold=20.0, desired_oaf=10.0, ventilation_oaf_threshold=5.0, in- sufficient_damper_threshold=15.0, temp_damper_threshold=90.0, rated_cfm=6000.0, eer=10.0, **kwargs) Bases: volttron.platform.agent.driven.AbstractDrivenAgent Application to detect and correct operational problems for AHUs/RTUs. This application uses metered data from zones server by an AHU/RTU to detect operational problems and where applicable correct these problems by modifying set points. When auto-correction cannot be applied then a message detailing the diagnostic results will be made available to the building operator. pre_message(dx_result, cur_time) Handle reporting of diagnostic pre-requisite messages. Report to user when conditions are not favorable for a diagnostic. run(cur_time, points) Main run method that is called by the DrivenBaseClass. run receives a dictionary of data ‘points’ and an associated timestamp for the data cur_time’. run then passes the appropriate data to each diagnostic when calling the diagnostic message.

10.2. Applications 273 VOLTTRON Documentation, Release 3.5RC1

class economizer.economizer_rcx.EconCorrectlyOff(data_window, no_required_data, min_damper_sp, ex- cess_damper_threshold, cool- ing_enabled_threshold, desired_oaf, cfm, eer) Bases: object Air-side HVAC economizer diagnostic for AHU/RTU systems. EconCorrectlyOff uses metered data from a BAS or controller to diagnose if an AHU/RTU is economizing when it should not. clear_data(dx_result) reinitialize class insufficient_oa data econ_alg3(dx_result, oatemp, ratemp, matemp, damper_signal, econ_condition, cur_time, fan_sp, cooling_call) Check app. pre-quisites and assemble data set for analysis. economizing_when_not_needed(dx_result, cur_time) If the detected problems(s) are consistent then generate a fault message(s). class economizer.economizer_rcx.EconCorrectlyOn(oaf_economizing_threshold, open_damper_threshold, data_window, no_required_data, cfm, eer) Bases: object Air-side HVAC economizer diagnostic for AHU/RTU systems. EconCorrectlyOn uses metered data from a BAS or controller to diagnose if an AHU/RTU is economizing when it should. clear_data(dx_result) reinitialize class insufficient_oa data. econ_alg2(dx_result, cooling_call, oatemp, ratemp, matemp, damper_signal, econ_condition, cur_time, fan_sp) Check app. pre-quisites and assemble data set for analysis. max_dx_time = None Application result messages not_economizing_when_needed(dx_result, cur_time) If the detected problems(s) are consistent then generate a fault message(s). class economizer.economizer_rcx.ExcessOA(data_window, no_required_data, ex- cess_oaf_threshold, min_damper_sp, ex- cess_damper_threshold, desired_oaf, cfm, eer) Bases: object Air-side HVAC ventilation diagnostic. ExcessOA uses metered data from a controller or BAS to diagnose when an AHU/RTU is providing excess outdoor air. clear_data(dx_result) reinitialize class insufficient_oa data. econ_alg4(dx_result, oatemp, ratemp, matemp, damper_signal, econ_condition, cur_time, fan_sp, cooling_call) Check app. pre-quisites and assemble data set for analysis. excess_oa(dx_result, cur_time) If the detected problems(s) are consistent generate a fault message(s).

274 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1 class economizer.economizer_rcx.InsufficientOA(data_window, no_required_data, ventila- tion_oaf_threshold, min_damper_sp, insuf- ficient_damper_threshold, desired_oaf ) Bases: object Air-side HVAC ventilation diagnostic. insufficient_oa_intake uses metered data from a controller or BAS to diagnose when an AHU/RTU is providing inadequate ventilation. clear_data(dx_result) reinitialize class insufficient_oa data. econ_alg5(dx_result, oatemp, ratemp, matemp, damper_signal, econ_condition, cur_time, cool- ing_call) Check app. pre-quisites and assemble data set for analysis. insufficient_oa(dx_result, cur_time) If the detected problems(s) are consistent generate a fault message(s). timestamp = None Application thresholds (Configurable) class economizer.economizer_rcx.TempSensorDx(data_window, no_required_data, temp_diff_thr, open_damper_time, oat_mat_check, temp_damper_threshold) Bases: object Air-side HVAC temperature sensor diagnostic for AHU/RTU systems. TempSensorDx uses metered data from a BAS or controller to diagnose if any of the temperature sensors for an AHU/RTU are accurate and reliable. clear_data(dx_result) reinitialize class insufficient_oa data econ_alg1(dx_result, oatemp, ratemp, matemp, damper_signal, cur_time) Check app. pre-quisites and assemble data set for analysis. max_dx_time = None Application thresholds (Configurable) temperature_sensor_dx(dx_result, cur_time) If the detected problems(s) are consistent then generate a fault message(s).

Module contents

10.2.8 EventPerformanceAgent eventperformance package

Submodules eventperformance.agent module class eventperformance.agent.EventPerformanceAgent(config_path, **kwargs) Bases: volttron.platform.agent.base.PublishMixin, volttron.platform.agent.base.BaseAgent listens for and fulfills requests for generated event performance statistics

10.2. Applications 275 VOLTTRON Documentation, Release 3.5RC1

on_request(topic, headers, message, match) process request for event performance process_request(arg_set) setup() eventperformance.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

Module contents

10.2.9 FakeDrivenMatlabAgent drivenmatlab package

Submodules drivenmatlab.drivenagent module drivenmatlab.drivenagent.driven_agent(config_path, **kwargs) Reads agent configuration and converts it to run driven agent. :param kwargs: Any driver specific parameters drivenmatlab.drivenagent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method. drivenmatlab.drivenagent_pubsub module drivenmatlab.drivenagent_pubsub.driven_agent(config_path, **kwargs) Driven harness for deployment of OpenEIS applications in VOLTTRON. drivenmatlab.drivenagent_pubsub.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘lan- guage=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method. drivenmatlab.matlab module

Copyright (c) 2014, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution.

276 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, r favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 class drivenmatlab.matlab.Application(**kwargs) Bases: volttron.platform.agent.driven.AbstractDrivenAgent run(cur_time, points) Sends device points to Matlab application and waits for response. Creates and returns Results object from received response from Matlab application. :param cur_time: timestamp :param points: device point name and value :type cur_time: datetime.datetime :type points: dict :Returns Results object containing commands for devices, log messages and table data.

Rtype results Results object volttron.platform.agent.driven

Module contents

10.2.10 ILCAgent ilc package

Submodules ilc.agent module class ilc.agent.BaseCriterion(minimum=None, maximum=None) Bases: object evaluate()

10.2. Applications 277 VOLTTRON Documentation, Release 3.5RC1

evaluate_bounds(value) evaluate_criterion() ingest_data(time_stamp, data) class ilc.agent.Clusters Bases: object add_device_cluster(cluster) get_device(device_name) get_device_name_list() get_score_order() reset_currently_curtailed() reset_curtail_count() class ilc.agent.ConditionalCurtailment(condition=None, conditional_args=None, **kwargs) Bases: object check_condition() get_curtailment() ingest_data(data) class ilc.agent.ConstantCriterion(value=None, off_value=0.0, point_name=None, **kwargs) Bases: ilc.agent.BaseCriterion evaluate() class ilc.agent.Criteria(criteria) Bases: object add(name, criterion) evaluate() get_curtailment() increment_curtail() ingest_data(time_stamp, data) reset_currently_curtailed() reset_curtail_count() class ilc.agent.CurtailmentManager(conditional_curtailment_settings=[], **kwargs) Bases: object get_curtailment() ingest_data(data) class ilc.agent.CurtailmentSetting(point=None, value=None, load=None, offset=None) Bases: object get_curtailment_dict() ingest_data(data) class ilc.agent.Device(device_config) Bases: object

278 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

evaluate(command) get_curtailment(command) get_on_commands() increment_curtail(command) ingest_data(time_stamp, data) reset_currently_curtailed() reset_curtail_count() class ilc.agent.DeviceCluster(priority, crit_labels, row_average, cluster_config) Bases: object get_all_device_evaluations() class ilc.agent.FormulaCriterion(operation=None, operation_args=None, **kwargs) Bases: ilc.agent.BaseCriterion evaluate() ingest_data(time_stamp, data) class ilc.agent.HistoryCriterion(comparison_type=None, point_name=None, previ- ous_time=None, **kwargs) Bases: ilc.agent.BaseCriterion evaluate() ingest_data(time_stamp, data) linear_interpolation(date1, value1, date2, value2, target_date) class ilc.agent.MapperCriterion(dict_name=None, map_key=None, **kwargs) Bases: ilc.agent.BaseCriterion evaluate() class ilc.agent.StatusCriterion(on_value=None, off_value=0.0, point_name=None, **kwargs) Bases: ilc.agent.BaseCriterion evaluate() ingest_data(time_stamp, data) ilc.agent.ilc_agent(config_path, **kwargs) Intelligent Load Curtailment (ILC) Application using Analytical Hierarchical Process (AHP). ilc.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called to start the agent. ilc.agent.register_criterion(name) ilc.ilc_matrices module ilc.ilc_matrices.build_score(_matrix, weight, priority) Calculates the curtailment score using the normalized matrix m, and the weights vector returns a sorted vector of weights for each device that is a candidate for curtailment.

10.2. Applications 279 VOLTTRON Documentation, Release 3.5RC1 ilc.ilc_matrices.calc_column_sums(criteria_matrix) Calculate the column sums for the criteria matrix. ilc.ilc_matrices.extract_criteria(excel_file, sheet) Function to extract the criteria matrix from the CriteriaMatrix sheet of the excel spreadsheet ilc.ilc_matrices.input_matrix(builder, criteria_labels) Construct input normalized input matrix. ilc.ilc_matrices.normalize_matrix(criteria_matrix, col_sums) Normalizes the members of criteria matrix using the vector col_sums. Returns the normalized matrix, and the sums of each row of the matrix. ilc.ilc_matrices.validate_input(pairwise_matrix, col_sums, criteria_LABELS=’‘, CRI- TERIA_LABELSTRING=’‘, MATRIX_ROWSTRING=’‘, display_dest=’, mode ‘w’>) Validates the criteria matrix to ensure that the inputs are internally consistent. Returns a True if the matrix is valid, and False if it is not.

Module contents

10.2.11 MasterNode-and-ModelNode-Agents

10.2.12 MatlabProxy matlab_proxy package

Submodules matlab_proxy.agent module matlab_proxy.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called to start the agent. matlab_proxy.agent.matlab_proxy_agent(config_path, **kwargs)

280 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Module contents

10.2.13 ModbusXMLSchema

10.2.14 MpcAgent mpc package

Submodules mpc.CBC_Gui module class mpc.CBC_Gui.CBC_Gui(building) Bases: threading.Thread exit() run() class mpc.CBC_Gui.ZoneGui(zone, building, root)

select_fan(e) select_heat_cool(e) mpc.MPC module class mpc.MPC.MPC

cleanup() get_outdoor_temp() make_gui() run_control(simHrs) set_outdoor_temp(degF) mpc.MPC.scale_time(seconds) mpc.agent module class mpc.agent.MpcAgent(config_path, **kwargs) Bases: volttron.platform.agent.base.PublishMixin, volttron.platform.agent.base.BaseAgent

on_match(topic, headers, message, match) run_control() setup()

10.2. Applications 281 VOLTTRON Documentation, Release 3.5RC1 mpc.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. mpc.python_building module class mpc.python_building.Building

advance(dtHrs) cleanup() get_high_temp_limit(zone) get_indoor_temp(zone) get_low_temp_limit(zone) get_num_zones() get_outdoor_temp() set_deadbands(zone, cool, heat) set_fan_mode(zone, mode) set_hvac_mode(zone, mode) set_outdoor_temp(degf ) mpc.python_control module class mpc.python_control.Control(numZones)

cleanup() get_control_period() get_hvac_command(zone) run_control() set_lower_limit(zone, degsC) set_max_units(units) set_outside_temp(degsC) set_upper_limit(zone, degsC) set_zone_temp(zone, degsC)

282 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Module contents

10.2.15 OpenADRAgent

openadr package

Submodules

openadr.agent module

openadr.agent.OpenADRAgent(config_path, **kwargs) openadr.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

Module contents

10.2.16 PassiveAFDD passiveafdd package

Submodules passiveafdd.passive_afdd module passiveafdd.passive_afdd.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called to start the agent. passiveafdd.passive_afdd.passiveafdd(config_path, **kwargs) Passive fault detection application for AHU/RTU economizer systems

Module contents

10.2.17 SMDSAgent

smds package

Submodules

smds.agent module

smds.agent.SMDSAgent(config_path, **kwargs) smds.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

10.2. Applications 283 VOLTTRON Documentation, Release 3.5RC1

smds.settings module

Module contents

10.2.18 SMDSPushAgent

smdspush package

Submodules

smdspush.agent module

class smdspush.agent.Connection(**kwargs) Bases: object get_points() get_values(chan_id, after=None, before=None) smdspush.agent.DatetimeFromValue(ts) smdspush.agent.PushAgent(config_path, **kwargs) smdspush.agent.TimestampFromDatetime(dt) smdspush.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

284 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Module contents

10.2.19 agents

10.3 Examples

10.3.1 AlertMonitor alertmonitor package

Submodules alertmonitor.agent module

Module contents

10.3.2 Alerter alerter package

Submodules alerter.agent module class alerter.agent.AlerterAgent(config_path, **kwargs) Bases: volttron.platform.vip.agent.Agent The AlerterAgent is a simple agent that allows control via rpc to send an alert and update it’s heartbeat. send_alert1(key, message) starting(sender, **kwargs) status_bad(context) status_good() alerter.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called to start the agent.

10.3. Examples 285 VOLTTRON Documentation, Release 3.5RC1

Module contents send module

10.3.3 AskAgent askbot package

Submodules askbot.agent module

VOLTTRON platform™ example service for user interaction. Opens a listening socket on 127.0.0.1:7575 to interact with the user by displaying the current state and asking for a new one. When the new state is entered, the new state is printed and the next state is requested. State transitions are sent to the VOLTTRON message bus on the topic ‘askbot/state’ as a two-tuple (previous_state, current_state). The listening address, backlog count, and initial state can be configured through the agent configuration file. Many security and error checks are omitted for clarity. Please do not use this agent without validating all connections and inputs. Connect to the agent using one of the following commands: nc 127.0.0.1 7575 netcat 127.0.0.1 7575 socat - TCP-CONNECT:127.0.0.1:7575 telnet 127.0.0.1 7575 class askbot.agent.AskAgent(config_path, **kwargs) Bases: volttron.platform.agent.base.PublishMixin, volttron.platform.agent.base.BaseAgent Example agent to demonstrate user interaction. ask_input(file) Send the current state and ask for a new one. change_state(state) Change state and notify other agents. handle_accept(ask_sock) Accept new connections. handle_input(file) Recieve the new state from the client and ask for another. setup() Perform additional setup. askbot.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called to start the agent.

286 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Module contents

10.3.4 CAgent

c_agent package

Submodules

c_agent.agent module

c_agent.cdriver module

Module contents

10.3.5 DataPublisher

datapublisher package

Submodules

datapublisher.publisher module

datapublisher.publisher.DataPub(config_path, **kwargs) Emulate device driver to publish data and Actuator agent for agent testing. datapublisher.publisher.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

datapublisher.publisher3 module

datapublisher.publisher3.DataPub(config_path, **kwargs) Emulate device driver to publish data and Actuatoragent for testing. The first column in the data file must be the timestamp and it is not published to the bus unless the config option: ‘maintain_timestamp’ - True will allow the publishing of specified timestamps.

False will use the current now time and publish using it. datapublisher.publisher3.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

10.3. Examples 287 VOLTTRON Documentation, Release 3.5RC1

Module contents

10.3.6 ExampleDrivenControlAgent example package

Submodules example.drivenagent module example.drivenagent.DrivenAgent(config_path, **kwargs) Driven harness for deployment of OpenEIS applications in VOLTTRON. example.drivenagent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method. example.drivencontrol module

Copyright (c) 2014, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, r favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof.

288 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPART- MENT OF ENERGY under Contract DE-AC05-76RL01830 class example.drivencontrol.Application(**kwargs) Bases: volttron.platform.agent.driven.AbstractDrivenAgent Example application for showing a driven application issuing control commands run(current_time, points)

Module contents

10.3.7 ExampleMatlabApplication

10.3.8 ExampleSubscriber subscriber package

Submodules subscriber.settings module subscriber.subscriber_agent module subscriber.subscriber_agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. subscriber.subscriber_agent.subscriber_agent(config_path, **kwargs)

Module contents

10.3.9 ListenerAgent listener package

Submodules listener.agent module class listener.agent.ListenerAgent(config_path, **kwargs) Bases: volttron.platform.vip.agent.Agent Listens to everything and publishes a heartbeat according to the heartbeat period specified in the settings module.

on_match(peer, sender, bus, topic, headers, message) Use match_all to receive all messages and print them out. onsetup(sender, **kwargs) onstart(sender, **kwargs)

10.3. Examples 289 VOLTTRON Documentation, Release 3.5RC1 listener.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. listener.settings module

Module contents

10.3.10 MobilityExample mobileexample package

Submodules mobileexample.agent module mobileexample.agent.MobileExampleAgent(config_path, **kwargs) mobileexample.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the platform.

Module contents

10.3.11 MultiNodeExample greeter package

Submodules greeter.agent module class greeter.agent.GreeterAgent(config_path, **kwargs) Bases: volttron.platform.agent.base.PublishMixin, volttron.platform.agent.base.BaseAgent Publishes a hello message to all receiving platforms on_match(topic, headers, message, match) Use match_all to receive all messages and print them out. publish_heartbeat() setup() greeter.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

290 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

greeter.settings module

Module contents

10.3.12 PingPongAgent pingpong package

Submodules pingpong.agent module pingpong.agent.PingPongAgent(config_path, **kwargs) Agent to demonstrate agent-initiated mobility. The agent periodically calculates the next host to visit from the hosts list specified in the config file and requests a move. pingpong.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

Module contents

10.3.13 ProcessAgent launcher package

Submodules launcher.agent module class launcher.agent.ProcessAgent(subscribe_address, process, **kwargs) Bases: volttron.platform.agent.base.BaseAgent finish() poll_process() setup() launcher.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable.

10.3. Examples 291 VOLTTRON Documentation, Release 3.5RC1

launcher.settings module

Module contents

10.3.14 SchedulerExample schedule_example package

Submodules schedule_example.agent module schedule_example.agent.DatetimeFromValue(ts) Utility for dealing with time schedule_example.agent.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. schedule_example.agent.schedule_example(config_path, **kwargs) schedule_example.settings module

Module contents

10.3.15 SimpleForwarder simpleforwarder package

Submodules simpleforwarder.simpleforwarder module simpleforwarder.simpleforwarder.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘lan- guage=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) Main method called by the eggsecutable. simpleforwarder.simpleforwarder.simpleforwarder(config_path, **kwargs)

Module contents

10.3.16 StandAloneListener settings module settings.remote_url()

292 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1 standalonelistener module

This python script will listen to the defined vip address for specific topics. The user can modify the settings.py file to set the specific topics to listen to. With a volttron activated shell this script can be run like: python standalonelistener.py This script prints all output to standard out rather than using the logging facilities. This script will also publish a heart beat (which will be returned if lisenening to the heartbeat topic). Example output to standard out (not the starting at {” it will all be on a single line: publishing heartbeat. {“topic”: “heartbeat/standalonelistener”, “headers”: {“Date”: “2015-10-22 15:22:43.184351Z”, “Content-Type”: “text/plain”}, “mes- sage”: “2015-10-22 15:22:43.184351Z”} {“topic”: “devices/building/campus/hotwater/heater/resistive/information/power/part_realpwr_avg”, “headers”: {“Date”: “2015-10-22 00:45:15.480339”}, “message”: [{“part_realpwr_avg”: 0.0}, {“part_realpwr_avg”: {“units”: “percent”, “tz”: “US/Pacific”, “type”: “float”}}]} The heartbeat message is a simple plain text message with just a datestamp A “data” message contains an array of 2 elements. The first element contains a dictionary of (point name: value) pairs. The second element contains context around the point data and the “Date” header. class standalonelistener.StandAloneListener(identity=None, address=None, con- text=None, publickey=None, secretkey=None, serverkey=None, heartbeat_autostart=False, heartbeat_period=60) Bases: volttron.platform.vip.agent.Agent A standalone version of the ListenerAgent onmessage(peer, sender, bus, topic, headers, message) Handle incoming messages on the bus. publish_heartbeat() Send heartbeat message every heartbeat_period seconds. heartbeat_period is set and can be adjusted in the settings module. start(sender, **kwargs) Handle the starting of the agent. Subscribe to all points in the topics_prefix_to_watch tuple defined in settings.py.

10.3.17 StandAloneWithAuth settings module settings.remote_url() standalonewithauth module

{

10.3. Examples 293 VOLTTRON Documentation, Release 3.5RC1

“allow”: [ {“credentials”: “CURVE:XR-l7nMBB1zDRsUS2Mjb9lePkcNsgoosHKpCDm6D9TI”, “domain”: “vip”, “address”: “127.0.0.1”, “capabilities”: [”can_call_bar”]} ] } (You will still need to change the ‘server_key’ value in settings.py) class standalonewithauth.StandAloneWithAuth(identity=None, address=None, con- text=None, publickey=None, secretkey=None, serverkey=None, heartbeat_autostart=False, heartbeat_period=60) Bases: volttron.platform.vip.agent.Agent A standalone agent that demonstrates how to use agent authorization bar() call_foo_and_bar() foo()

10.3.18 configurations

10.4 Volttron

10.4.1 volttron volttron package

Subpackages volttron.drivers package

Submodules volttron.drivers.bacnet module Copyright (c) 2015, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY

294 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. class volttron.drivers.bacnet.BACnet(smap_instance, attach_point, namespace) Bases: volttron.drivers.base.BaseSmapVolttron get_interface(opts) setup(opts) class volttron.drivers.bacnet.BACnetInterface(self_address, target_address, max_apdu_len=1024, seg_supported=’segmentedBoth’, obj_id=599, obj_name=’sMap BACnet driver’, ven_id=15, config_file=’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/local/lib/python2.7/site- packages/volttron-3.5.0- py2.7.egg/volttron/drivers/bacnet_example_config.csv’, **kwargs) Bases: volttron.drivers.base.BaseInterface get_point_async(point_name) get_point_sync(point_name) insert_register(register) parse_config(config_file) ping_target(address) scrape_all() scrape_all_callback(result) set_point_async(point_name, value) set_point_sync(point_name, value) setup_device(address, max_apdu_len=1024, seg_supported=’segmentedBoth’, obj_id=599, obj_name=’sMap BACnet driver’, ven_id=15) class volttron.drivers.bacnet.BACnetRegister(instance_number, object_type, property_name, read_only, pointName, units, description=’‘) Bases: volttron.drivers.base.BaseRegister get_state_async(bac_app, address) get_state_sync(bac_app, address) set_state_async(bac_app, address, value) set_state_async_callback(result, set_value) set_state_sync(bac_app, address, value) class volttron.drivers.bacnet.BACnet_application(*args) Bases: bacpypes.app.BIPSimpleApplication, bacpypes.task.RecurringTask confirmation(apdu)

10.4. Volttron 295 VOLTTRON Documentation, Release 3.5RC1

get_next_invoke_id(addr) Called to get an unused invoke ID. process_task() request(iocb) submit_request(iocb) class volttron.drivers.bacnet.IOCB(request) volttron.drivers.bacnet.block_for_sync(d, timeout=None) volttron.drivers.base module Copyright (c) 2015, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. class volttron.drivers.base.BaseInterface(**kwargs) Bases: object build_register_map() get_point_async(point_name) get_point_sync(point_name) get_register_by_name(name) get_register_names() get_registers_by_type(reg_type, read_only) insert_register(register) scrape_all() Should return either a dictionary of point names:values Alternatively it can return a deferred that will produce the same thing. set_point_async(point_name, value) set_point_sync(point_name, value)

296 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1 class volttron.drivers.base.BaseRegister(register_type, read_only, pointName, units, descrip- tion=’‘) Bases: object get_description() get_register_python_type() get_register_type() Get (type, read_only) tuple get_units() class volttron.drivers.base.BaseSmapVolttron(smap_instance, attach_point, namespace) Bases: smap.driver.SmapDriver, volttron.platform.agent.base.PublishMixin get_interface(opts) get_paths_for_point(point) read() read_callback(results) read_errback(failure) setup(opts) start() class volttron.drivers.base.InterfaceBitActuator(new_uuid, unit, read_limit=0, write_limit=0, autoadd=True, **tsargs) Bases: smap.actuate.BinaryActuator SMAP bit actuator that uses the abstract driver interface to touch points get_state(request) set_state(request, state) setup(opts) class volttron.drivers.base.InterfaceFloatActuator(*args, **kwargs) Bases: smap.actuate.IntegerActuator SMAP float actuator that uses the abstract driver interface to touch points ACTUATE_MODEL = ‘continuous’ get_state(request) parse_state(state) set_state(request, state) setup(opts) valid_state(state) class volttron.drivers.base.InterfaceIntActuator(*args, **kwargs) Bases: smap.actuate.IntegerActuator SMAP integer actuator that uses the abstract driver interface to touch points get_state(request) set_state(request, state) setup(opts)

10.4. Volttron 297 VOLTTRON Documentation, Release 3.5RC1

volttron.drivers.data_logger module class volttron.drivers.data_logger.DataLogger(smap_instance, attach_point, namespace) Bases: smap.driver.SmapDriver docstring for DataLogger read() setup(opts) setup_publisher() setup_subscriber() start() subscribe()

volttron.drivers.file_driver module Copyright (c) 2015, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. class volttron.drivers.file_driver.File(smap_instance, attach_point, namespace) Bases: volttron.drivers.base.BaseSmapVolttron Fake device backed by a file for each register. Designed to use the modbus configuration file for setup. get_interface(opts) setup(opts) class volttron.drivers.file_driver.FileInterface(directory=’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/local/lib/python2.7/site- packages/volttron-3.5.0- py2.7.egg/volttron/drivers’, config_file=’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/local/lib/python2.7/site- packages/volttron-3.5.0- py2.7.egg/volttron/drivers/example.csv’, **kwargs) Bases: volttron.drivers.base.BaseInterface

298 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

get_point_async(point_name) get_point_sync(point_name) parse_config(directory, config_file) scrape_all() set_point_async(point_name, value) set_point_sync(point_name, value) class volttron.drivers.file_driver.FileRegister(type_string, read_only, pointName, units, description=’‘, directory=’.’) Bases: volttron.drivers.base.BaseRegister get_value() parse_value(value_string) set_value(value) volttron.drivers.modbus module Copyright (c) 2015, Battelle Memorial Institute All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow- ing disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, IN- CIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSI- NESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CON- TRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAM- AGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the FreeBSD Project. class volttron.drivers.modbus.Modbus(smap_instance, attach_point, namespace) Bases: volttron.drivers.base.BaseSmapVolttron get_interface(opts) setup(opts) class volttron.drivers.modbus.ModbusBitRegister(address, type_string, pointName, units, read_only, description=’‘, slave_id=0) Bases: volttron.drivers.modbus.ModbusRegisterBase get_register_count() get_state_async(client) get_state_callback(response_bits) get_state_sync(client)

10.4. Volttron 299 VOLTTRON Documentation, Release 3.5RC1

parse_value(starting_address, bit_stream) set_state_async(client, value) set_state_callback(response) set_state_sync(client, value) class volttron.drivers.modbus.ModbusByteRegister(address, type_string, pointName, units, read_only, description=’‘, slave_id=0) Bases: volttron.drivers.modbus.ModbusRegisterBase get_register_count() get_state_async(client) get_state_callback(response) get_state_sync(client) parse_value(starting_address, byte_stream) set_state_async(client, value) set_state_callback(value, client) set_state_sync(client, value) class volttron.drivers.modbus.ModbusInterface(ip_address, port=502, slave_id=0, config_file=’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/local/lib/python2.7/site- packages/volttron-3.5.0- py2.7.egg/volttron/drivers/example.csv’, **kwargs) Bases: volttron.drivers.base.BaseInterface build_ranges_map() close_async_connection_later(client) get_point_async(point_name) get_point_sync(point_name) insert_register(register) parse_config(config_file) scrape_all() scrape_bit_registers(client, read_only) scrape_byte_registers(client, read_only) set_point_async(point_name, value) set_point_sync(point_name, value) exception volttron.drivers.modbus.ModbusInterfaceException(string) Bases: pymodbus.exceptions.ModbusException class volttron.drivers.modbus.ModbusRegisterBase(address, register_type, read_only, pointName, units, description=’‘, slave_id=0) Bases: volttron.drivers.base.BaseRegister

Module contents

300 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1 volttron.platform package

Subpackages volttron.platform.agent package

Submodules volttron.platform.agent.base module VOLTTRON platform™ base agent and helper classes/functions. volttron.platform.agent.base.periodic(period, *args, **kwargs) Decorator to set a method up as a periodic callback. The decorated method will be called with the given arguments every period seconds while the agent is executing its run loop. class volttron.platform.agent.base.BaseAgent(subscribe_address, **kwargs) Bases: volttron.platform.agent.base.AgentBase Base class for creating VOLTTRON platform™ agents. This class can be used as is, but it won’t do much. It will sit and do nothing but listen for messages and exit when the platform shutdown message is received. That is it. LOOP_INTERVAL = 60 closed Return whether the subscription channel is closed. finish() Finish for the agent execution loop. Extend this method with code that must run once after the main loop. Be sure to call the base class implementation from the overridden method. handle_sub_message(block=False) Handle incoming messages on the subscription socket. Receives a multipart message containing a topic, headers, and zero or more message parts. For each prefix (key) in subscriptions map matching the beginning of the topic, the associated callback will be called if either no test is associated with the callback or the test function returns a value evaluating to True. See the class documentation for more information on the signature for test and callback functions. loop() Main agent execution loop. This method should rarely need to be overridden. Instead, override the step method to customize execu- tion behavior. The default implementation loops until self.closed() returns True calling self.step() each iteration. periodic_timer(period, function, *args, **kwargs) Create a periodic timer to call function every period seconds. Like the timer method except that the timer is automatically rearmed after the function completes. poll(timeout=None) Polls for events while handling timers.

10.4. Volttron 301 VOLTTRON Documentation, Release 3.5RC1

poll() will wait up to timeout seconds for sockets or files registered with self.reactor to become ready. A timeout of None will cause poll to wait an infinite amount of time. While waiting for poll events, scheduled events will be handled, potentially causing the wait time to slip a bit. run() Entry point for running agent. Subclasses should not override this method. Instead, the setup, step, and finish methods should be overrid- den to customize behavior. schedule(time, event) Schedule an event to run at the given wall time. time must be a datetime object or a Unix time value as returned by time.time(). event must be a callable accepting a single argument, the time the event was scheduled to run, and must return a time to be sched- uled next or None to not reschedule. sched.Event and sched.RecurringEvent are examples of this interface and may be used here. Generators send functions are also be good candidates for event functions. setup() Setup for the agent execution loop. Extend this method with code that must run once before the main loop. Be sure to call the base class implementation from the overridden method. step(timeout=None) Performs a single step in the main agent loop. Override this method to customize agent behavior. The default method blocks indefinitely until at least one socket in the reactor is ready and then run each associated callback. The method can be called from the overridden method in a subclass with the behavior customized by passing in different timeout. timeout is the maximum number of seconds (can be fractional) to wait or None to wait indefinitely. Returns the number of events fired or zero if a timeout occured. subscribe(prefix, callback=None, test=None) Subscribe to topic and register callback. Subscribes to topics beginning with prefix. If callback is supplied, it should be a function taking four arguments, callback(topic, headers, message, match), where topic is the full message topic, headers is a case-insensitive dictionary (mapping) of message headers, message is a possibly empty list of message parts, and match is the return value of the test function or None if test is None. If test is given, it should be a function taking two arguments, test(topic, prefix), where topic is the complete topic of the incoming message and prefix is the string which caused the subscription match. The test function should return a true value if the callback should be called or a false value otherwise. The result of the test will be passed into the callback function where the results can be used. Returns and ID number which can be used later to unsubscribe. timer(interval, function, *args, **kwargs) Create a timer to call function after interval seconds. interval is specified in seconds and can include fractional part. function is a function that takes the optional args and kwargs. Returns a timer object that can be used to modify the callback parameters or to cancel using the cancel() method. unsubscribe(handler_id, prefix=None) Remove subscription handler by its ID. Remove all handlers matching the given handler ID, which is the ID returned by the subscribe method. If all handlers for a topic prefix are removed, the topic is also unsubscribed.

302 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

unsubscribe_all(prefix) Remove all handlers for the given prefix and unsubscribe. If prefix is None, unsubscribe from all topics and remove all handlers. Otherwise, unsubscribe from the given topic and remove all handlers for that topic prefix. class volttron.platform.agent.base.PublishMixin(publish_address, **kwargs) Bases: volttron.platform.agent.base.AgentBase Agent mix-in for publishing to the VOLTTRON publish socket. Connects the agent to the publish channel and provides several publish methods. Include before BaseAgent class in subclass list. ping_back(callback, timeout=None, period=1) publish(topic, headers, *msg_parts, **kwargs) Publish a message to the publish channel. Adds volttron platform version compatibility information to header as variables min_compatible_version and max_compatible version publish_ex(topic, headers, *msg_tuples, **kwargs) Publish messages given as (content-type, message) tuples. Adds volttron platform version compatibility information to header as variables min_compatible_version and max_compatible version publish_json(topic, headers, *msg_parts, **kwargs) Publish JSON encoded message. Adds volttron platform version compatibility information to header as variables min_compatible_version and max_compatible version volttron.platform.agent.base_historian module

Historian Development Support for storing and retrieving historical device and analysis data published to the mes- sage bus is handled with Historian Agents. If a new type of data store or a new way of storing data is desired a new type of Historian Agent should created. Historian Agents are implemented by subclassing BaseHistorian. Agents that need short term storage of device data should subscribe to device data and use internal data structures for storage. Agents which need long term Historical data that predates the startup of the Agent should interact with a Historian Agent in order to obtain that data as needed. While it is possible to create an Agent from scratch which handles gathering and storing device data it will miss out on the benefits of creating a proper Historian Agent that subclassing BaseHistorian. The BaseHistorian class provides the following features: • A separate thread for all communication with a data store removing the need to use or implement special libraries to work with gevent. • Automatically subscribe to and process device publishes. • Automatically backup data retrieved off the message bus to a disk cache. Cached data will only be removed once it is successfully published to a data store. • Existing Agents that publish analytical data for storage or query for historical data will be able to use the new Historian without any code changes. • Data can be graphed in VOLTTRON Central.

10.4. Volttron 303 VOLTTRON Documentation, Release 3.5RC1

Creating a New Historian To create a new Historian create a new Agent that subclasses BaseHistorian. BaseHistorian inherits from volttron.platform.vip.agent.Agent so including it in the class parents is not needed. The new Agent must implement the following methods: • BaseHistorianAgent.publish_to_historian() • BaseQueryHistorianAgent.query_topic_list() • BaseQueryHistorianAgent.query_historian() While not required this method may be overridden as needed: - BaseHistorianAgent.historian_setup() Optionally a Historian Agent can inherit from BaseHistorianAgent instead of BaseHistorian if support for querying data is not needed for the data store. If this route is taken then VOLTTRON Central will not be able to graph data from the store. It is possible to run more than one Historian agent at a time to store data in more than one place. If needed one can be used to allow querying while another is used to put data in the desired store that does not allow querying.

Historian Execution Flow At startup the BaseHistorian class starts a new thread to handle all data caching and publishing (the publishing thread). The main thread then subscribes to all Historian related topics on the message bus. Whenever subscribed data comes in it is published to a Queue to be be processed by the publishing thread as soon as possible. At startup the publishing thread calls BaseHistorianAgent.historian_setup() to give the implemented Historian a chance to setup any connections in the thread. The process thread then enters the following logic loop: Wait for data to appear in the Queue. Proceed if data appears or a `retry_period` time elapses. If new data appeared in Queue: Save new data to cache. While data is in cache: Publish data to store by calling :py:meth:`BaseHistorianAgent.publish_to_historian`. If no data was published: Go back to start and check Queue for data. Remove published data from cache. If we have been publishing for `max_time_publishing`: Go back to start and check Queue for data.

The logic will also forgo waiting the retry_period for new data to appear when checking for new data if publishing has been successful and there is still data in the cache to be publish.

Storing Data The BaseHistorian will call BaseHistorianAgent.publish_to_historian() as the time series data becomes available. Data is batched in a groups up to submit_size_limit. After processing the list or individual items in the list BaseHistorianAgent.publish_to_historian() must call BaseHistorianAgent.report_handled() to report an individual point of data was published or BaseHistorianAgent.report_all_handled() to report that everything from the batch was successfully published. This tells the BaseHistorianAgent class what to remove from the cache and if any publishing was successful. The to_publish_list argument of BaseHistorianAgent.publish_to_historian() is a list of records that takes the following form:

304 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

As records are published to the data store BaseHistorianAgent.publish_to_historian() must call BaseHistorianAgent.report_handled() with the record or list of records that was published or BaseHistorianAgent.report_all_handled() if everything was published.

Querying Data When an request is made to query data the BaseQueryHistorianAgent.query_historian() method is called. When a request is made for the list of topics in the store BaseQueryHistorianAgent.query_topic_list() will be called.

Other Notes Implemented Historians must be tolerant to receiving the same data for submission twice. While very rare, it is possible for a Historian to be forcibly shutdown after data is published but before it is removed from the cache. When restarted the BaseHistorian will submit the same date over again. class volttron.platform.agent.base_historian.BackupDatabase(owner, backup_storage_limit_gb) A creates and manages backup cache for the BaseHistorianAgent class. Historian implementors do not need to use this class. It is for internal use only. backup_new_data(new_publish_list) Parameters new_publish_list (list) – A list of records to cache to disk. get_outstanding_to_publish(size_limit) Retrieve up to size_limit records from the cache. Parameters size_limit (int) – Max number of records to retrieve. Returns List of records for publication. Return type list remove_successfully_published(successful_publishes, submit_size) Removes the reported successful publishes from the backup database. If None is found in success- ful_publishes we assume that everything was published. Parameters • successful_publishes (list) – List of records that was published. • submit_size (int) – Number of things requested from previous call to get_outstanding_to_publish(). class volttron.platform.agent.base_historian.BaseHistorian(**kwargs) Bases: volttron.platform.agent.base_historian.BaseHistorianAgent, volttron.platform.agent.base_historian.BaseQueryHistorianAgent class volttron.platform.agent.base_historian.BaseHistorianAgent(retry_period=300.0, sub- mit_size_limit=1000, max_time_publishing=30, backup_storage_limit_gb=None, topic_replace_list=None, **kwargs) Bases: volttron.platform.vip.agent.Agent This is the base agent for historian Agents. It automatically subscribes to all device publish topics. Event processing occurs in its own thread as to not block the main thread. Both the historian_setup and pub- lish_to_historian happen in the same thread. By default the base historian will listen to 4 separate root topics ( datalogger/, record/, actuators/, and device/. Messages that are published to actuator are assumed to be part of the actuation process. Messages published to

10.4. Volttron 305 VOLTTRON Documentation, Release 3.5RC1

datalogger will be assumed to be timepoint data that is composed of units and specific types with the assumption that they have the ability to be graphed easily. Messages published to devices are data that comes directly from drivers. Finally Messages that are published to record will be handled as string data and can be customized to the user specific situation. This base historian will cache all received messages to a local database before publishing it to the historian. This allows recovery for unexpected happenings before the successful writing of data to the historian. historian_setup() Optional setup routine, run in the processing thread before main processing loop starts. Gives the Historian a chance to setup connections in the publishing thread. publish_to_historian(to_publish_list) Main publishing method for historian Agents. Parameters to_publish_list (list) – List of records to_publish_list takes the following form: The contents of meta is not consistent. The keys in the meta data values can be different and can change along with the values of the meta data. It is safe to assume that the most recent value of the “meta” dictionary are the only values that are relevant. This is the way the cache treats meta data. Once one or more records are published either BaseHistorianAgent.report_handled() or BaseHistorianAgent.report_handled() must be called to report records as being published. report_all_handled() Call this from BaseHistorianAgent.publish_to_historian() to report that all records passed to BaseHistorianAgent.publish_to_historian() have been successfully published and should be removed from the cache. report_handled(record) Call this from BaseHistorianAgent.publish_to_historian() to report a record or list of records has been successfully published and should be removed from the cache. Parameters record (dict or list) – Record or list of records to remove from cache. starting_base(sender, **kwargs) Subscribes to the platform message bus on the actuator, record, datalogger, and device topics to capture data. stopping(sender, **kwargs) Release subscription to the message bus because we are no longer able to respond to messages now. class volttron.platform.agent.base_historian.BaseQueryHistorianAgent(identity=None, ad- dress=None, con- text=None, pub- lickey=None, se- cretkey=None, serverkey=None, heart- beat_autostart=False, heart- beat_period=60) Bases: volttron.platform.vip.agent.Agent This is the base agent for historian Agents that support querying of their data stores.

306 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

get_topic_list() RPC call Returns List of topics in the data store. Return type list query(topic=None, start=None, end=None, skip=0, count=None, order=’FIRST_TO_LAST’) RPC call Call this method to query an Historian for time series data. Parameters • topic (str) – Topic to query for. • start (str) – Start time of the query. Defaults to None which is the beginning of time. • end (str) – End time of the query. Defaults to None which is the end of time. • skip (int) – Skip this number of results. • count (int) – Limit results to this value. • order (str) – How to order the results, either “FIRST_TO_LAST” or “LAST_TO_FIRST” Returns Results of the query Return type dict Return values will have the following form: { "values": [(: value1), (: value2), ...], "metadata":{"key1": value1, "key2": value2, ...} }

The string arguments can be either the output from volttron.platform.agent.utils.format_timestamp() or the special string “now”. Times relative to “now” may be specified with a relative time string using the Unix “at”-style specifications. For instance “now -1h” will specify one hour ago. “now -1d -1h -20m” would specify 25 hours and 20 minutes ago. query_historian(topic, start=None, end=None, skip=0, count=None, order=None) This function is called by BaseQueryHistorianAgent.query() to actually query the data store and must return the results of a query in the form: { "values": [(timestamp1: value1), (timestamp2: value2), ...], "metadata":{"key1": value1, "key2": value2, ...} }

Timestamps must be strings formatted by volttron.platform.agent.utils.format_timestamp(). “metadata” is not required. The caller will normalize this to {} for you if it is missing.

10.4. Volttron 307 VOLTTRON Documentation, Release 3.5RC1

Parameters • topic (str) – Topic to query for. • start (datetime) – Start of query timestamp as a datetime. • end (datetime) – End of query timestamp as a datetime. • skip (int) – Skip this number of results. • count (int) – Limit results to this value. • order (str) – How to order the results, either “FIRST_TO_LAST” or “LAST_TO_FIRST” Returns Results of the query Return type dict query_topic_list() This function is called by BaseQueryHistorianAgent.get_topic_list() to actually topic list from the data store. Returns List of topics in the data store. Return type list volttron.platform.agent.base_historian.get_timeunit(t) volttron.platform.agent.base_historian.is_number(x) volttron.platform.agent.base_historian.now(tzstr=’UTC’) Returns an aware datetime object with the current time in tzstr timezone volttron.platform.agent.base_historian.p_abstime(t) abstime : NUMBER | QSTRING | NOW volttron.platform.agent.base_historian.p_error(p) volttron.platform.agent.base_historian.p_query_pair(t) query : ‘(‘ timeref ‘,’ timeref ‘)’ volttron.platform.agent.base_historian.p_query_single(t) query : timeref volttron.platform.agent.base_historian.p_reltime(t) reltime : NUMBER LVALUE | NUMBER LVALUE reltime volttron.platform.agent.base_historian.p_timeref(t) timeref : abstime | abstime reltime volttron.platform.agent.base_historian.parse_time(ts) volttron.platform.agent.base_historian.strptime_tz(str, format=’%x %X’, tzstr=’Local’) Returns an aware datetime object. tzstr is a timezone string such as ‘US/Pacific’ or ‘Local’ by default which uses the local timezone. volttron.platform.agent.base_historian.t_LVALUE(t) [a-zA-Z~$_][a-zA-Z0-9/%_-]* volttron.platform.agent.base_historian.t_NUMBER(t) ([+-]?([0-9]*.)?[0-9]+) volttron.platform.agent.base_historian.t_QSTRING(t) (“[^”\]*?(\.[^”\]*?)*?”)|(‘[^’\]*?(\.[^’\]*?)*?’)

308 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

volttron.platform.agent.base_historian.t_error(t) volttron.platform.agent.base_historian.t_newline(t) [nr]+

volttron.platform.agent.cron module cron-like schedule generator. volttron.platform.agent.cron.schedule(cron_string, start=None, stop=None) Return a schedule generator from a cron-style string. cron_string is a cron-style time expression consisting of five whitespace-separated fields explained in further detail below. start and stop are used to bound the schedule and can be None, datetime.datetime objects or numeric values, such as is returned by time.time(). stop may also be supplied as a datetime.timedelta object, in which case the end time is start + stop. If start is None, the current time is used. If stop is None, schedule will generate values infinitely. Each iteration yields a datetime.datetime object. The following description of the cron fields is taken from the crontab(5) man page (with slight modifications). The time and date fields are: field allowed values —– ————– minute 0-59 hour 0-23 day of month 1-31 month 1-12 (or names, see below) day of week 0-7 (0 or 7 is Sunday, or use names) A field may contain an asterisk (*), which always stands for “first-last”. Ranges of numbers are allowed. Ranges are two numbers separated with a hyphen. The specified range is inclusive. For example, 8-11 for an ‘hours’ entry specifies execution at hours 8, 9, 10, and 11. If the range start or end value is left off, the first or last value will be used. For example, -8 for an ‘hours’ entry is equivalent to 0-8, 20- for a ‘days of month’ entry is equivalent to 20-31, and - for a ‘months’ entry is equivalent to 1-12. Lists are allowed. A list is a set of numbers (or ranges) separated by commas. Examples: “1,2,5,9”, “0-4,8-12”. Step values can be used in conjunction with ranges. Following a range with “/” specifies skips of the number’s value through the range. For example, “0-23/2” can be used in the ‘hours’ field to specify every other hour. Step values are also permitted after an asterisk, “*/2” in the ‘hours’ field is equivalent to “0-23/2”. Names can also be used for the ‘month’ and ‘day of week’ fields. Use at least the first three letters of the particular day or month (case does not matter). Note: The day can be specified in the following two fields: ‘day of month’, and ‘day of week’. If both fields are restricted (i.e., do not contain the “*” character), then both are used to compute date/time values. For example, “30 4 1,15 * 5” is interpreted as “4:30 am on the 1st and 15th of each month, plus every Friday.” volttron.platform.agent.driven module VOLTTRON platform™ abstract agent for to drive VOLTTRON Nation apps. class volttron.platform.agent.driven.AbstractDrivenAgent(out=None, **kwargs) Bases: object classmethod output_format(input_object) The output object takes the resulting input object as a argument so that it may give correct topics to it’s outputs if needed. output schema description {TableName1: {name1:OutputDescriptor1, name2:OutputDescriptor2,...},....} eg: {‘OAT’: {‘Timestamp’:OutputDescriptor(‘timestamp’, ‘foo/bar/timestamp’),’OAT’:OutputDescriptor(‘OutdoorAirTemperature’, ‘foo/bar/oat’)}, ‘Sensor’: {‘SomeValue’:OutputDescriptor(‘integer’, ‘some_output/value’), ‘SomeOtherValue’:OutputDescriptor(‘boolean’, ‘some_output/value), ‘SomeString’:OutputDescriptor(‘string’, ‘some_output/string)}}

10.4. Volttron 309 VOLTTRON Documentation, Release 3.5RC1

Should always call the parent class output_format and update the dictionary returned from the parent. result = super().output_format(input_object) my_output = {...} result.update(my_output) return result run(time, inputs) Do work for each batch of timestamped inputs time- current time inputs - dict of point name -> value Must return a results object. shutdown() Override this to add shutdown routines. class volttron.platform.agent.driven.ConversionMapper(**kwargs) Bases: object process_row(row_dict) setup_conversion_map(conversion_map_config, field_names) class volttron.platform.agent.driven.Results(terminate=False) Bases: object command(point, value, device=None) insert_table_row(table, row) log(message, level=10) terminate(terminate) volttron.platform.agent.green module VOLTTRON platform™ greenlet coroutine helper classes/functions. These utilities are meant to be used with the BaseAgent and greenlet to provide synchronization between light threads (coroutines). exception volttron.platform.agent.green.Timeout Bases: exceptions.Exception Raised in the greenlet when waiting on a channel times out. class volttron.platform.agent.green.WaitQueue(create_timer) Bases: object A holder for tasklets waiting on asynchronous data. kill_all() Kill all the tasks in the queue. notify(data, n=1) Notify n waiting tasks of the arrival of data. notify_all(data) Notify all waiting tasks of the arrival of data. wait(timeout=None) Wait for data to become available and return it If timeout is None, wait indefinitely. Otherwise, timeout if the task hasn’t been notified within timeout seconds. volttron.platform.agent.green.sleep(timeout, create_timer) Yield execution for timeout seconds. volttron.platform.agent.known_identities module

310 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

volttron.platform.agent.matching module VOLTTRON platform™ topic matching for agent callbacks. Declaratively attach topic prefix and additional tests for topic matching to agent methods allowing for automated callback registration and topic subscription. Example: class MyAgent(BaseAgent): @match_regex(‘topic1/(sub|next|part)/title[1-9]’) def on_subtopic(topic, headers, message, match): # This is only executed if topic matches regex ... @match_glob(‘root/sub/*/leaf’) def on_leafnode(topic, headers, message, match): # This is only executed if topic matches glob ... @match_exact(‘building/xyz/unit/condenser’) @match_start(‘campus/PNNL’) @match_end(‘unit/blower’) def on_multimatch(topic, headers, message, match): # Multiple matchers can be attached to a method ... volttron.platform.agent.matching.iter_match_tests(obj) Iterate match tests attached to the methods of an object. Each iterated item is the 3-tuple (prefix, method, test) where prefix and test are the same as in match_test() and method is the method to which the test was attached (and is the expected callback). volttron.platform.agent.matching.match_all(func) Wildcard matcher to register callback for every message. volttron.platform.agent.matching.match_contains(substring, prefix=’‘) Return a match decorator to match a component of a topic. volttron.platform.agent.matching.match_end(suffix, prefix=’‘) Return a match decorator to match the end of a topic. volttron.platform.agent.matching.match_exact(topic) Return a match decorator to match a topic exactly. volttron.platform.agent.matching.match_glob(pattern) Return a match decorator for the given glob pattern. volttron.platform.agent.matching.match_headers(required_headers) Only call function if required headers match. match_headers takes a single argument, required_headers, that is a dictionary containing the required headers and values that must match for the wrapped handler function to be called. This decorator is not very useful on its own, because it doesn’t trigger any subscriptions, but can be useful to filter out messages that don’t contain the required headers and values. volttron.platform.agent.matching.match_regex(pattern) Return a match decorator for the given regular expression. volttron.platform.agent.matching.match_start(prefix) Return a match decorator to match the start of a topic. volttron.platform.agent.matching.match_subtopic(prefix, subtopic, max_levels=None) Return a match decorator to match a subtopic. volttron.platform.agent.matching.match_test(prefix, test=None) Decorate a callback method with subscription and test information. Returns a decorator to attach (prefix, test) 2-tuples to methods which can be inspected to automatically subscribe to a topic prefix and provide a test for triggering a call back to the method.

10.4. Volttron 311 VOLTTRON Documentation, Release 3.5RC1

prefix must match the start of a desired topic and test is either None or a function of the form test(topic, matched) where topic is the full topic to test against and matched should be the same as prefix. The test function must return a value that evaluates to True if the topic is a match or a value that evaluates to False otherwise. The test function is only called if topic.startswith(prefix) is True. If test is None, it is the same as if test = lambda topic, matched: True. volttron.platform.agent.matching.test_contains(substring) Return a test function to match a topic containing substring. volttron.platform.agent.matching.test_end(suffix) Return a test function to match the end of a topic. volttron.platform.agent.matching.test_exact(topic, matched) Test if topic and match are exactly equal. volttron.platform.agent.matching.test_glob(pattern) Return static prefix and regex test for glob pattern. The pattern may include the following special wildcard patterns: •Matches zero or more characters.

** Matches zero or more characters, including forward slashes (/).

? Matches any single character [...] Matches any single characters between the brackets. A range of adjacent characters may be matched using a hyphen (-) between the start and end character. To include the hyphen as a search character, include it at the end of the pattern. The range may be negated by immediately following the opening [ with a ^ or !. volttron.platform.agent.matching.test_regex(pattern) Return the static prefix and a regex test function for pattern. volttron.platform.agent.matching.test_subtopic(subtopic, max_levels=None) Return a test function to match a topic component after the prefix. volttron.platform.agent.math_utils module Dumping ground for VOLTTRON platform™ agent math helper func- tions. Not meant to replace numpy in all cases. A basic set common math routines to remove the need for numpy in simple cases. This module should NEVER import numpy as that would defeat the purpose. volttron.platform.agent.math_utils.mean(data) Return the sample arithmetic mean of data. volttron.platform.agent.math_utils.pstdev(data) Calculates the population standard deviation. volttron.platform.agent.math_utils.stdev(data) Calculates the sample standard deviation. volttron.platform.agent.multithreading module VOLTTRON platform™ multi-threaded agent helper classes/functions. These utilities are meant to be used with the BaseAgent and threading to provide synchronization between threads and the main agent loop.

312 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

exception volttron.platform.agent.multithreading.Timeout Bases: exceptions.Exception Raised in the thread when waiting on a queue times out. class volttron.platform.agent.multithreading.WaitQueue(lock=None) Bases: object A holder for threads waiting on asynchronous data. notify(data, n=1) Notify n waiting threads of the arrival of data. notify_all(data) Notify all waiting threads of the arrival of data. wait(timeout=None) Wait for data to become available and return it If timeout is None, wait indefinitely. Otherwise, timeout if the thread hasn’t been notified within timeout seconds. volttron.platform.agent.sched module VOLTTRON platform™ agent event scheduling classes. class volttron.platform.agent.sched.Event(function, args=None, kwargs=None) Bases: object Base class for schedulable objects. args cancel() Mark the timer as canceled to avoid a callback. canceled finished function kwargs class volttron.platform.agent.sched.EventWithTime(function, args=None, kwargs=None) Bases: volttron.platform.agent.sched.Event Event that passes deadline to event handler. class volttron.platform.agent.sched.Queue Bases: object delay(time) execute(time) schedule(time, event) class volttron.platform.agent.sched.RecurringEvent(period, function, args=None, kwargs=None) Bases: volttron.platform.agent.sched.Event period

10.4. Volttron 313 VOLTTRON Documentation, Release 3.5RC1

volttron.platform.agent.utils module VOLTTRON platform™ agent helper classes/functions. volttron.platform.agent.utils.load_config(config_path) Load a JSON-encoded configuration file. volttron.platform.agent.utils.run_agent(cls, subscribe_address=None, pub- lish_address=None, config_path=None, **kwargs) Instantiate an agent and run it in the current thread. Attempts to get keyword parameters from the environment if they are not set. volttron.platform.agent.utils.start_agent_thread(cls, **kwargs) Instantiate an agent class and run it in a new daemon thread. Returns the thread object.

Module contents volttron.platform.lib package

Subpackages volttron.platform.lib.inotify package

Submodules volttron.platform.lib.inotify.green module gevent-safe interface to Linux inotify system calls. class volttron.platform.lib.inotify.green.inotify(flags=0) Bases: volttron.platform.lib.inotify._inotify read()

Module contents Interface to Linux inotify system calls. class volttron.platform.lib.inotify.inotify(flags=0) Bases: volttron.platform.lib.inotify._inotify

Submodules volttron.platform.lib.kwonlyargs module Support functions for implementing keyword-only arguments. This module is designed to make it easy to support keyword-only arguments in Python 2.7 while providing the same kind of exceptions one would see with Python 3.x. Basic usage: def foo(arg1,*args,**kwargs): # Use required context manager to convert KeyError exceptions # to TypeError with an appropriate message. with required: arg2 = kwargs.pop(‘arg2’) arg3 = kwargs.pop(‘arg3’) # Provide a default to pop for optional arguments arg4 = kwargs.pop(‘arg4’, ‘default value’) # In- clude the next line to disallow additional keyword args assertempty(kwargs)

314 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

volttron.platform.lib.kwonlyargs.assertempty(kwargs) Raise TypeError if kwargs is not empty.

volttron.platform.lib.prctl module Python interface to Linux process control mechanism. Exports prctl system call. See also prctl(2). volttron.platform.lib.prctl.prctl(option, *args) Perform control operations on a process using prctl(2). Perform control operations on a process by passing in one of the PR_GET_* or PR_SET_* options and any additional arguments as specified by the prctl documentation. The result varies based on the option. An OSError exception is raised on error. See also prctl(2).

Module contents

volttron.platform.messaging package

Submodules

volttron.platform.messaging.headers module VOLTTRON platform™ messaging header name constants. class volttron.platform.messaging.headers.Headers(*args, **kwargs) Bases: dict Case-insensitive dictionary for HTTP-like headers. class Key Bases: str Headers.copy() Headers.dict Return a dictionary with originally-cased keys. Headers.get(key, default=None) Headers.setdefault(key, value) Headers.update(*args, **kwargs)

volttron.platform.messaging.health module class volttron.platform.messaging.health.Status Bases: object The Status objects wraps the context status and last reported into a small object that can be serialized and sent across the zmq message bus. There are two static methods for constructing Status objects: • from_json() Expects a json string as input. • build() Expects at least a status in the ACCEPTABLE_STATUS tuple. The build() method also takes a context and a callback function that will be called when the status changes.

10.4. Volttron 315 VOLTTRON Documentation, Release 3.5RC1

as_dict() Returns a copy of the status object properties as a dictionary. @return: as_json() Serializes the object to a json string. Note: Does not serialize the change callback function.

Returns

static build(status, context=None, status_changed_callback=None) Constructs a Status object and initializes its state using the passed parameters. Parameters • status – • context – • status_changed_callback – Returns context static from_json(data, status_changed_callback=None) Deserializes a Status object and returns it to the caller. Parameters • data – • status_changed_callback – Returns last_updated status update_status(status, context=None) Updates the internal state of the Status object. This method will throw errors if the context is not serializable or if the status parameter is not within the ACCEPTABLE_STATUS tuple. Parameters • status – • context – Returns volttron.platform.messaging.socket module VOLTTRON platform™ messaging classes. class volttron.platform.messaging.socket.Headers(*args, **kwargs) Bases: dict Case-insensitive dictionary for HTTP-like headers. class Key Bases: str

316 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Headers.copy() Headers.dict Return a dictionary with originally-cased keys. Headers.get(key, default=None) Headers.setdefault(key, value) Headers.update(*args, **kwargs) class volttron.platform.messaging.socket.Socket(socket_type, context=None) Bases: zmq.sugar.socket.Socket ØMQ socket with additional agent messaging methods. recv_message(flags=0) Recieve a message as (topic, headers, message) tuple. recv_message_ex(flags=0) Receive a message as (content type, message) tuples. Like recv_message(), returns a three tuple. However, the final message component contains a list of 2- tuples instead of a list of messages. These 2-tuples contain the content- type and the data. send_message(topic, headers, *msg_parts, **kwargs) Send a multipart message with topic and headers. Send a multipart message on the socket with topic being a UTF-8 string, headers can be a dictionary or a Headers object, and msg_parts is the optional parts of the message. The media or content type of each message component should be included in the ‘Content-Type’ header which should be a list of MIME types or a string if there is only one message part. send_message_ex(topic, headers, *msg_tuples, **kwargs) Send messages given as (content-type, message) tuples. Similar to the send_message method except that messages are given as 2-tuples with the MIME type as the first element and the message data as the second element. send_string(u, flags=0, copy=True, encoding=’utf-8’) send a Python unicode string as a message with an encoding 0MQ communicates with raw bytes, so you must encode/decode text (unicode on py2, str on py3) around 0MQ. u [Python unicode string (unicode on py2, str on py3)] The unicode string to send. flags [int, optional] Any valid send flag. encoding [str [default: ‘utf-8’]] The encoding to be used volttron.platform.messaging.topics module VOLTTRON platform™ topic templates. Templates of standard topics. Fields in the templates are replaced by calling the template with the field value included in the keyword arguments. Fields are replaced from left to right as long as a replacement can be made. Once a field is reached which cannot be replaced, everything in the replaced portion up to the last double slash is returned. Fields cannot be skipped, but may be included unsubstituted by using None for the field value. Below are some examples to demonstrate. >>> T=_('root/{top}//{middle}//{bottom}') >>> T() Topic(u'root') >>> T(top='first')

10.4. Volttron 317 VOLTTRON Documentation, Release 3.5RC1

Topic(u'root/first') >>> T(top='first', middle='second') Topic(u'root/first/second') >>> T(top='first', middle='second', bottom='third') Topic(u'root/first/second/third') >>> unicode(T(top='first', middle='second', bottom='third')) u'root/first/second/third' >>> T(top='first', bottom='third') ValueError: unused keyword argument: bottom >>> T(top='first', middle=None, bottom='third') Topic(u'root/first/{middle}/third') >>> T(top='first', middle=None, bottom='third')(middle='.') Topic(u'root/first/third') >>> T(top='first', middle=None, bottom='third')(middle='..') Topic(u'root/third') >>> T._(top='first', middle=None, bottom='third') Topic(u'root/first//{middle}//third') volttron.platform.messaging.utils module VOLTTRON platform™ messaging utilities. volttron.platform.messaging.utils.normtopic(topic) Normalize topic, removing extra slashes and dots. class volttron.platform.messaging.utils.Topic(format_string) Bases: unicode format(**kwargs) vformat(kwargs)

Module contents volttron.platform.vip package

Subpackages volttron.platform.vip.agent package

Subpackages volttron.platform.vip.agent.subsystems package

Submodules volttron.platform.vip.agent.subsystems.base module class volttron.platform.vip.agent.subsystems.base.SubsystemBase Bases: object

318 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1 volttron.platform.vip.agent.subsystems.channel module class volttron.platform.vip.agent.subsystems.channel.Channel(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase ADDRESS = ‘inproc://subsystem/channel’ create(peer, name=None) volttron.platform.vip.agent.subsystems.health module class volttron.platform.vip.agent.subsystems.health.Health(owner, core, rpc) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase get_status() “RPC method Returns the last updated status from the object with the context. The minimum output from the status would be: { “status”: “GOOD”, “context”: None, “utc_last_update”: “2016-03- 31T15:40:32.685138+0000” } send_alert(alert_key, statusobj) An alert_key is a quasi-unique key. A listener to the alert can determine whether to pass the alert on to a higher level based upon the frequency of this alert. Parameters • alert_key – • context – Returns set_status(status, context=None) RPC method Updates the agents status to the new value with the specified context. Param status: str: GODD, BAD Param context: str: A serializable that denotes the context of status. volttron.platform.vip.agent.subsystems.heartbeat module class volttron.platform.vip.agent.subsystems.heartbeat.Heartbeat(owner, core, rpc, pubsub, heart- beat_autostart, heart- beat_period) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase publish() restart() RPC method Restart the heartbeat with the current period. The heartbeat will be immediately sending the heartbeat to the message bus.

10.4. Volttron 319 VOLTTRON Documentation, Release 3.5RC1

set_period(period) RPC method Set heartbeat period. Parameters period – Time in seconds between publishes. start() RPC method Starts an agent’s heartbeat. start_with_period(period) RPC method Set period and start heartbeat. Parameters period – Time in seconds between publishes. stop() RPC method Stop an agent’s heartbeat.

volttron.platform.vip.agent.subsystems.hello module class volttron.platform.vip.agent.subsystems.hello.Hello(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase The hello subsystem allows an agent to determine its identity. The identity is possibly a dynamically generated uuid from which the executing agent does not know. This subsystem allows the agent to be able to determine it’s identity from a peer. By default that peer is the connected router, however this could be another agent. hello(peer=’‘) Receives a welcome message from the peer (default to ‘’ router) The welcome message will respond with a 3 element list: •The vip version (default 1.0) •The peer who responded (should be the same as peer argument to this function. •The id of the requester (i.e. this object). This will be the identity when the agent connects to the router or the specified identity when the Agent is constructed.

Parameters peer – The peer to receive the response from. Returns [version, peer, identity] volttron.platform.vip.agent.subsystems.peerlist module class volttron.platform.vip.agent.subsystems.peerlist.PeerList(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase list() volttron.platform.vip.agent.subsystems.ping module class volttron.platform.vip.agent.subsystems.ping.Ping(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase ping(peer, *args)

320 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1 volttron.platform.vip.agent.subsystems.pubsub module class volttron.platform.vip.agent.subsystems.pubsub.PubSub(core, rpc_subsys, peerlist_subsys, owner) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase add_bus(name) add_subscription(peer, prefix, callback, bus=’‘) drop_subscription(peer, prefix, callback, bus=’‘) list(peer, prefix=’‘, bus=’‘, subscribed=True, reverse=False) publish(peer, topic, headers=None, message=None, bus=’‘) Publish a message to a given topic via a peer. Publish headers and message to all subscribers of topic on bus at peer. If peer is None, use self. Adds volttron platform version compatibility information to header as variables min_compatible_version and max_compatible version remove_bus(name) subscribe(peer, prefix, bus=’‘) synchronize(peer) Unsubscribe from stale/forgotten/unsolicited subscriptions. unsubscribe(peer, prefix, callback, bus=’‘) Unsubscribe and remove callback(s). Remove all handlers matching the given handler ID, which is the ID returned by the subscribe method. If all handlers for a topic prefix are removed, the topic is also unsubscribed. volttron.platform.vip.agent.subsystems.query module class volttron.platform.vip.agent.subsystems.query.Query(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase query(prop, peer=’‘) volttron.platform.vip.agent.subsystems.rpc module class volttron.platform.vip.agent.subsystems.rpc.RPC(core, owner) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase allow(capabilities) Decorator specifies required agent capabilities to call a method. This is designed to be used with the export decorator: @RPC.export @RPC.allow(‘can_read_status’) def get_status(): ... Multiple capabilies can be provided in a list: @RPC.allow([’can_read_status’, ‘can_call_my_methods’]) batch(peer, requests) call(peer, method, *args, **kwargs) export(name=None) notify(peer, method, *args, **kwargs)

10.4. Volttron 321 VOLTTRON Documentation, Release 3.5RC1

Module contents class volttron.platform.vip.agent.subsystems.PeerList(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase list() class volttron.platform.vip.agent.subsystems.Ping(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase ping(peer, *args) class volttron.platform.vip.agent.subsystems.RPC(core, owner) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase allow(capabilities) Decorator specifies required agent capabilities to call a method. This is designed to be used with the export decorator: @RPC.export @RPC.allow(‘can_read_status’) def get_status(): ... Multiple capabilies can be provided in a list: @RPC.allow([’can_read_status’, ‘can_call_my_methods’]) batch(peer, requests) call(peer, method, *args, **kwargs) export(name=None) notify(peer, method, *args, **kwargs) class volttron.platform.vip.agent.subsystems.Hello(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase The hello subsystem allows an agent to determine its identity. The identity is possibly a dynamically generated uuid from which the executing agent does not know. This subsystem allows the agent to be able to determine it’s identity from a peer. By default that peer is the connected router, however this could be another agent. hello(peer=’‘) Receives a welcome message from the peer (default to ‘’ router) The welcome message will respond with a 3 element list: •The vip version (default 1.0) •The peer who responded (should be the same as peer argument to this function. •The id of the requester (i.e. this object). This will be the identity when the agent connects to the router or the specified identity when the Agent is constructed.

Parameters peer – The peer to receive the response from. Returns [version, peer, identity] class volttron.platform.vip.agent.subsystems.PubSub(core, rpc_subsys, peerlist_subsys, owner) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase add_bus(name) add_subscription(peer, prefix, callback, bus=’‘) drop_subscription(peer, prefix, callback, bus=’‘)

322 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

list(peer, prefix=’‘, bus=’‘, subscribed=True, reverse=False) publish(peer, topic, headers=None, message=None, bus=’‘) Publish a message to a given topic via a peer. Publish headers and message to all subscribers of topic on bus at peer. If peer is None, use self. Adds volttron platform version compatibility information to header as variables min_compatible_version and max_compatible version remove_bus(name) subscribe(peer, prefix, bus=’‘) synchronize(peer) Unsubscribe from stale/forgotten/unsolicited subscriptions. unsubscribe(peer, prefix, callback, bus=’‘) Unsubscribe and remove callback(s). Remove all handlers matching the given handler ID, which is the ID returned by the subscribe method. If all handlers for a topic prefix are removed, the topic is also unsubscribed. class volttron.platform.vip.agent.subsystems.Channel(core) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase ADDRESS = ‘inproc://subsystem/channel’ create(peer, name=None) class volttron.platform.vip.agent.subsystems.Heartbeat(owner, core, rpc, pubsub, heartbeat_autostart, heart- beat_period) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase publish() restart() RPC method Restart the heartbeat with the current period. The heartbeat will be immediately sending the heartbeat to the message bus. set_period(period) RPC method Set heartbeat period. Parameters period – Time in seconds between publishes. start() RPC method Starts an agent’s heartbeat. start_with_period(period) RPC method Set period and start heartbeat. Parameters period – Time in seconds between publishes. stop() RPC method Stop an agent’s heartbeat.

10.4. Volttron 323 VOLTTRON Documentation, Release 3.5RC1

class volttron.platform.vip.agent.subsystems.Health(owner, core, rpc) Bases: volttron.platform.vip.agent.subsystems.base.SubsystemBase get_status() “RPC method Returns the last updated status from the object with the context. The minimum output from the status would be: { “status”: “GOOD”, “context”: None, “utc_last_update”: “2016-03- 31T15:40:32.685138+0000” } send_alert(alert_key, statusobj) An alert_key is a quasi-unique key. A listener to the alert can determine whether to pass the alert on to a higher level based upon the frequency of this alert. Parameters • alert_key – • context – Returns set_status(status, context=None) RPC method Updates the agents status to the new value with the specified context. Param status: str: GODD, BAD Param context: str: A serializable that denotes the context of status.

Submodules volttron.platform.vip.agent.compat module class volttron.platform.vip.agent.compat.CompatPubSub(identity=None, ad- dress=None, context=None, peer=’pubsub’, pub- lish_address=’inproc://vip/compat/agent/publish’, sub- scribe_address=’inproc://vip/compat/agent/subscribe’) Bases: object VOLTTRON 2.x compatible agent pub/sub message exchange bus. Accept multi-part messages from sockets connected to in_addr, which is a PULL socket, and forward them to sockets connected to out_addr, which is a XPUB socket. When subscriptions are added or removed, a message of the form ‘subscriptions//’ is broadcast to the PUB socket where is either ‘add’ or ‘remove’ and is the topic being subscribed or unsubscribed. When a message is received of the form ‘subscriptions/list/’, a multipart message will be broadcast with the first two received frames (topic and headers) sent unchanged and with the remainder of the message containing currently subscribed topics which start with , each frame containing exactly one topic. PEER = ‘pubsub’ PUBLISH_ADDRESS = ‘inproc://vip/compat/agent/publish’

324 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

SUBSCRIBE_ADDRESS = ‘inproc://vip/compat/agent/subscribe’ forward(peer, sender, bus, topic, headers, message) in_loop(sender, **kwargs) out_loop(sender, **kwargs) setup(sender, **kwargs) volttron.platform.vip.agent.compat.unpack_legacy_message(headers, message) Unpack legacy pubsub messages for VIP agents. Loads JSON-formatted message parts and removes single-frame messages from their containing list. Does not alter headers.

volttron.platform.vip.agent.core module class volttron.platform.vip.agent.core.BasicCore(owner) Bases: object loop() periodic(period, args=None, kwargs=None, wait=0) classmethod receiver(signal) run(running_event=None) Entry point for running agent. schedule(deadline, *args, **kwargs) send(func, *args, **kwargs) send_async(func, *args, **kwargs) setup() spawn(func, *args, **kwargs) spawn_in_thread(func, *args, **kwargs) spawn_later(seconds, func, *args, **kwargs) stop(timeout=None) class volttron.platform.vip.agent.core.Core(owner, address=None, identity=None, con- text=None, publickey=None, secretkey=None, serverkey=None) Bases: volttron.platform.vip.agent.core.BasicCore connected handle_error(message) loop() register(name, handler, error_handler=None) volttron.platform.vip.agent.core.killing(*args, **kwds) Context manager to automatically kill spawned greenlets. Allows one to kill greenlets that would continue after a timeout: with killing(agent.vip.pubsub.subscribe( ‘peer’, ‘topic’, callback)) as subscribe: subscribe.get(timeout=10)

10.4. Volttron 325 VOLTTRON Documentation, Release 3.5RC1

volttron.platform.vip.agent.decorators module volttron.platform.vip.agent.decorators.annotate(obj, kind, name, value) volttron.platform.vip.agent.decorators.annotations(obj, kind, name) class volttron.platform.vip.agent.decorators.dualmethod(finstance=None, fclass=None, doc=None) Bases: object Descriptor to allow class and instance methods of the same name. This class implements a descriptor that works similar to the classmethod() built-ins and can be used as a deco- rator, like the property() built-in. Instead of a method being only a class or instance method, two methods can share the same name and be accessed as an instance method or a class method based on the context. Example: >>> class Foo(object): ... @dualmethod ... def bar(self): ... print 'instance method for', self ... @bar.classmethod ... def bar(cls): ... print 'class method for', cls ... >>> Foo.bar() class method for >>> Foo().bar() instance method for <__main__.Foo object at 0x7fcd744f6610> >>>

classmethod(fclass) Descriptor to set the class method. instancemethod(finstance) Descriptor to set the instance method. volttron.platform.vip.agent.decorators.spawn(method) Run a decorated method in its own greenlet, which is returned.

volttron.platform.vip.agent.dispatch module class volttron.platform.vip.agent.dispatch.Signal Bases: object connect(receiver, owner=None) disconnect(receiver) receiver(func) send(sender, **kwargs) sendby(executor, sender, **kwargs) volttron.platform.vip.agent.errors module exception volttron.platform.vip.agent.errors.VIPError(errnum, msg, peer, subsystem, *args) Bases: exceptions.Exception classmethod from_errno(errnum, msg, *args)

326 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1 exception volttron.platform.vip.agent.errors.Unreachable(errnum, msg, peer, subsys- tem, *args) Bases: volttron.platform.vip.agent.errors.VIPError exception volttron.platform.vip.agent.errors.Again(errnum, msg, peer, subsystem, *args) Bases: volttron.platform.vip.agent.errors.VIPError exception volttron.platform.vip.agent.errors.UnknownSubsystem(errnum, msg, peer, subsystem, *args) Bases: volttron.platform.vip.agent.errors.VIPError volttron.platform.vip.agent.example module class volttron.platform.vip.agent.example.ExampleAgent(identity=None, address=None, context=None, pub- lickey=None, secretkey=None, serverkey=None, heart- beat_autostart=False, heart- beat_period=60) Bases: volttron.platform.vip.agent.Agent baz() finish(sender, **kwargs) foo() goodbye(name) hello(name) onmessage(peer, sender, bus, topic, headers, message) saybye() sayhi() setup(sender, **kwargs) starting(sender, **kwargs) stopping(sender, **kwargs) volttron.platform.vip.agent.example.meh() volttron.platform.vip.agent.results module volttron.platform.vip.agent.results.counter(start=None, minimum=0, maxi- mum=18446744073709551615L) class volttron.platform.vip.agent.results.ResultsDictionary Bases: weakref.WeakValueDictionary next() volttron.platform.vip.agent.utils module volttron.platform.vip.agent.utils.build_agent(address=None, identity=None, pub- lickey=None, secretkey=None, time- out=10, serverkey=None, **kwargs) Builds a dynamic agent connected to the specifiedd address. Parameters • address –

10.4. Volttron 327 VOLTTRON Documentation, Release 3.5RC1

• identity – • publickey – • secretkey – • timeout – • kwargs – Returns

Module contents class volttron.platform.vip.agent.Agent(identity=None, address=None, context=None, pub- lickey=None, secretkey=None, serverkey=None, heartbeat_autostart=False, heartbeat_period=60) Bases: object class Subsystems(owner, core, heartbeat_autostart, heartbeat_period) Bases: object class volttron.platform.vip.agent.BasicAgent(**kwargs) Bases: object

Submodules volttron.platform.vip.green module VIP - VOLTTRON™ Interconnect Protocol implementation See https://github.com/VOLTTRON/volttron/wiki/VIP for protocol specification. This module is for use within gevent. It provides some locking around send operations to protect the VIP state. It should be safe to use a single socket in multiple greenlets without any kind of locking. class volttron.platform.vip.green.BaseRouter(context=None, default_user_id=None) Bases: volttron.platform.vip.router.BaseRouter class volttron.platform.vip.green.Socket(*args, **kwargs) Bases: volttron.platform.vip.socket._Socket, zmq.green.core._Socket volttron.platform.vip.router module class volttron.platform.vip.router.BaseRouter(context=None, default_user_id=None) Bases: object Abstract base class of VIP router implementation. Router implementers should inherit this class and implement the setup() method to bind to appropriate ad- dresses, set identities, setup authentication, etc, etc. The socket will be created by the start() method, which will then call the setup() method. Once started, the socket may be polled for incoming messages and those messages are handled/routed by calling the route() method. During routing, the issue() method, which may be implemented, will be called to allow for debugging and logging. Custom subsystems may be implemented in the handle_subsystem() method. The socket will be closed when the stop() method is called. handle_subsystem(frames, user_id) Handle additional subsystems and provide a response. This method does nothing by default and may be implemented by subclasses to provide additional subsys- tems. frames is a list of zmq.Frame objects with the following elements: [SENDER, RECIPIENT, PROTOCOL, USER_ID, MSG_ID, SUBSYSTEM, ...]

328 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

The return value should be None, if the subsystem is unknown, an empty list or False (or other False value) if the message was handled but does not require/generate a response, or a list of containing the following elements: [RECIPIENT, SENDER, PROTOCOL, USER_ID, MSG_ID, SUBSYSTEM, ...] issue(topic, frames, extra=None) lookup_user_id(sender, recipient, auth_token) Find and return a user identifier. Returns the UTF-8 encoded User-Id property from the sender frame or None if the authenticator did not set the User-Id metadata. May be extended to perform additional lookups. poll Returns the underlying socket’s poll method. route() Route one message and return. One message is read from the socket and processed. If the recipient is the router (empty recipient), the standard hello and ping subsystems are handled. Other subsystems are sent to handle_subsystem() for processing. Messages destined for other entities are routed appropriately. run() Main router loop. setup() Called from start() method to setup the socket. Implement this method to bind the socket, set identities and options, etc. start() Create the socket and call setup(). The socket is save in the socket attribute. The setup() method is called at the end of the method to perform additional setup. stop(linger=1) Close the socket. volttron.platform.vip.socket module VIP - VOLTTRON™ Interconnect Protocol implementation See https://github.com/VOLTTRON/volttron/wiki/VIP for protocol specification. This file contains an abstract _Socket class which should be extended to provide missing features for different threading models. The standard Socket class is defined in __init__.py. A gevent-friendly version is defined in green.py. class volttron.platform.vip.socket.Address(address, **defaults) Bases: object Parse and hold a URL-style address. The URL given by address may contain optional query string parameters and a URL fragment which, if given, will be interpreted as the socket identity for the given address. Valid parameters: server: Server authentication method; must be one of NULL, PLAIN, or CURVE. domain: ZAP domain for server authentication. serverkey: Encoded CURVE server public key. secretkey: Encoded CURVE secret key. publickey: Encoded CURVE public key. ipv6: Boolean value indicating use of IPv6. username: Username to use with PLAIN authentication. password: Password to use with PLAIN authentication.

10.4. Volttron 329 VOLTTRON Documentation, Release 3.5RC1

bind(sock, bind_fn=None) Extended zmq.Socket.bind() to include options in the address. connect(sock, connect_fn=None) Extended zmq.Socket.connect() to include options in the address. qs reset(sock) exception volttron.platform.vip.socket.ProtocolError Bases: exceptions.Exception Error raised for invalid use of Socket object. class volttron.platform.vip.socket.Message(**kwargs) Bases: object Message object returned form Socket.recv_vip_object(). volttron.platform.vip.socket.nonblocking(*args, **kwds) volttron.platform.vip.tracking module Utilities for tracking VIP message statistics at the router. class volttron.platform.vip.tracking.Tracker Bases: object Object for sharing data between the router and control objects. disable() Disable tracking. enable() Enable tracking. hit(topic, frames, extra) Increment counters for given topic and frames. reset() Reset all counters to default values and set start time.

Module contents VIP - VOLTTRON™ Interconnect Protocol implementation See https://github.com/VOLTTRON/volttron/wiki/VIP for protocol specification. This module is useful for using VIP outside of gevent. Please understand that ZeroMQ sockets are not thread-safe and care must be used when using across threads (or avoided all together). There is no locking around the state as there is with the gevent version in the green sub-module. class volttron.platform.vip.Socket(context=None, socket_type=5, shadow=None) Bases: volttron.platform.vip.socket._Socket, zmq.sugar.socket.Socket

Submodules volttron.platform.aip module Component for the instantiation and packaging of agents. class volttron.platform.aip.AIPplatform(env, **kwargs) Bases: object Manages the main workflow of receiving and sending agents.

330 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

active_agents() agent_identity(agent_uuid) Return the identity of the agent that is installed. The IDENTITY file is written to the agent’s install directory the the first time the agent is installed. This function reads that file and returns the read value. @param agent_uuid: @return: agent_name(agent_uuid) agent_priority(agent_uuid) agent_status(agent_uuid) agent_tag(agent_uuid) agent_uuid_from_pid(pid) autostart() check_resources(execreqs) clear_status(clear_all=False) config_dir finish() get_execreqs(agent_uuid) install_agent(agent_wheel) install_dir land_agent(agent_wheel) launch_agent(agent_path) list_agents() prioritize_agent(agent_uuid, priority=‘50’) publish_address remove_agent(agent_uuid) run_dir setup() Creates paths for used directories for the instance. shutdown() start_agent(agent_uuid) status_agents() stop_agent(agent_uuid) subscribe_address tag_agent(agent_uuid, tag) class volttron.platform.aip.ExecutionEnvironment Bases: object Environment reserved for agent execution.

10.4. Volttron 331 VOLTTRON Documentation, Release 3.5RC1

Deleting ExecutionEnvironment objects should cause the process to end and all resources to be returned to the system. execute(*args, **kwargs) class volttron.platform.aip.IgnoreErrno(errno, *more) Bases: object ignore = [] volttron.platform.aip.log_entries(name, agent, pid, level, stream) volttron.platform.aip.log_stream(name, agent, pid, path, stream) volttron.platform.aip.process_wait(p)

volttron.platform.async module Run gevent Greenlets in their own threads. Supports killing threads and executing callbacks from other threads. class volttron.platform.async.AsyncCall(hub=None) Bases: object Send functions to another thread’s gevent hub for execution. send(receiver, func, *args, **kwargs) Send a function to the hub to be called there. All the arguments to this method are placed in a queue and the hub is signaled that a function is ready. When the hub switches to this handler, the functions are iterated over, each being called with its results sent to the receiver. func is called with args and kwargs in the thread of the associated hub. If receiver is None, results are ignored and errors are printed when exceptions occur. Otherwise, receiver is called with the 2-tuple (exc_info, result). If an unhandled exception occurred, exc_info is the 3-tuple returned by sys.exc_info() and result is None. Otherwise exc_info is None and the result is func’s return value. Note that receiver is called from the hub’s thread and may need to be injected into the thread of the receiver. class volttron.platform.async.Threadlet(*args, **kwargs) Bases: threading.Thread A subclass of threading.Thread supporting gevent Greenlets. The run method is executed in the thread’s main greenlet and the thread will exit when that method returns. Other threads may run callbacks within this thread using the send() method. Unlike the base class, threading.Thread, daemon is set by default. kill(exception=) Raise GreenletExit or other exception in the main greenlet. send(callback, *args, **kwargs) Execute callback in this thread’s hub. exception volttron.platform.async.GreenletExit Bases: exceptions.BaseException

volttron.platform.auth module

332 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

class volttron.platform.auth.AuthEntry(domain=None, address=None, credentials=None, user_id=None, groups=None, roles=None, capa- bilities=None, comments=None, enabled=True, **kwargs) Bases: object add_capabilities(capabilities) match(domain, address, mechanism, credentials) static valid_credentials(cred) Raises AuthEntryInvalid if credentials are invalid exception volttron.platform.auth.AuthEntryInvalid Bases: volttron.platform.auth.AuthException Exception for invalid AuthEntry objects exception volttron.platform.auth.AuthException Bases: exceptions.Exception General exception for any auth error class volttron.platform.auth.AuthFile(auth_file=None) Bases: object add(auth_entry, overwrite=True) Adds an AuthEntry to the auth file Parameters • auth_entry (AuthEntry) – authentication entry • overwrite (bool) – set to true to overwrite matching entries

Warning: If overwrite is set to False and if auth_entry matches an existing entry then this method will raise AuthFileEntryAlreadyExists

find_by_credentials(credentials) Find all entries that have the given credentials Parameters credentials (str) – The credentials to search for Returns list of entries Return type list read() Gets the allowed entries, groups, and roles from the auth file. Returns tuple of allow-entries-list, groups-dict, roles-dict Return type tuple read_allow_entries() Gets the allowed entries from the auth file. Returns list of allow-entries Return type list remove_by_index(index) Removes entry from auth file by index Parameters index (int) – index of entry to remove

10.4. Volttron 333 VOLTTRON Documentation, Release 3.5RC1

Warning: Calling with out-of-range index will raise AuthFileIndexError

remove_by_indices(indices) Removes entry from auth file by indices Parameters indices (list) – list of indicies of entries to remove

Warning: Calling with out-of-range index will raise AuthFileIndexError

set_groups(groups) Define the mapping of group names to capability lists Parameters groups (dict) – dict where the keys are group names and the values are lists of capability names

Warning: Calling with invalid groups will raise ValueError

set_roles(roles) Define the mapping of role names to group lists Parameters roles – dict where the keys are role names and the values are lists of group names

Warning: Calling with invalid roles will raise ValueError

update_by_index(auth_entry, index) Updates entry will given auth entry at given index Parameters • auth_entry (AuthEntry) – new authorization entry • index (int) – index of entry to update

Warning: Calling with out-of-range index will raise AuthFileIndexError exception volttron.platform.auth.AuthFileEntryAlreadyExists(indicies, mes- sage=None) Bases: volttron.platform.auth.AuthFileIndexError Exception if adding an entry that already exists exception volttron.platform.auth.AuthFileIndexError(indices, message=None) Bases: volttron.platform.auth.AuthException, exceptions.IndexError Exception for invalid indices provided to AuthFile class volttron.platform.auth.AuthService(auth_file, aip, *args, **kwargs) Bases: volttron.platform.vip.agent.Agent authenticate(domain, address, mechanism, credentials) get_authorizations(user_id) RPC method Gets capabilities, groups, and roles for a given user. Parameters user_id (str) – user id field from VOLTTRON Interconnect Protocol Returns tuple of capabiliy-list, group-list, role-list

334 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Return type tuple get_capabilities(user_id) RPC method Gets capabilities for a given user. Parameters user_id (str) – user id field from VOLTTRON Interconnect Protocol Returns list of capabilities Return type list get_groups(user_id) RPC method Gets groups for a given user. Parameters user_id (str) – user id field from VOLTTRON Interconnect Protocol Returns list of groups Return type list get_roles(user_id) RPC method Gets roles for a given user. Parameters user_id (str) – user id field from VOLTTRON Interconnect Protocol Returns list of roles Return type list read_auth_file() setup_zap(sender, **kwargs) stop_zap(sender, **kwargs) unbind_zap(sender, **kwargs) zap_loop(sender, **kwargs) class volttron.platform.auth.List Bases: list match(value) class volttron.platform.auth.String Bases: unicode match(value) volttron.platform.auth.dump_user(*args) volttron.platform.auth.isregex(obj) volttron.platform.auth.load_user(string) volttron.platform.config module Advanced argument parser. Fully compatible with argparse, and can be used as a drop-in replacement, with the added ability add options from configuration files and environment variables. The default order of precedense is command-line > environment > config file > defaults. The only change which can be made to this order is to instruct the configuration file action to

10.4. Volttron 335 VOLTTRON Documentation, Release 3.5RC1 add options inline rather than at the beginning, causing configuration options to be parsed after environment variables and in the order encountered. class volttron.platform.config.AddConstAction(option_strings, dest, const=1, type=, default=None, required=False, help=None) Bases: argparse.Action Add a constant value to the option. class volttron.platform.config.ArgumentParser(*args, **kwargs) Bases: argparse.ArgumentParser add_help_argument(*args, **kwargs) add_version_argument(*args, **kwargs) get_switch(action, value, option_string) Convert argument-less options when they have an argument. Useful for allowing options in a configuration file and environment variables to be set or unset based on an argument which looks like a boolean. To be invert-able, the action must set its inverse attribute to an option that will invert the meaning. preprocess_option(action, namespace, arg_strings, option_string) Pre-process an action. If an action has a preprocess attribute which evaluates to True, then execute the action now. This type of action is expected to return None if it is to remain in the argument list unchanged. Otherwise, it should return two lists: the first will be appended to the configuration arguments and the second will replace the processed arguments. volttron.platform.config.CaseInsensitiveConfigFileAction(ConfigFileAction) class volttron.platform.config.ConfigFileAction(option_strings, dest, required=False, help=None, metavar=None, **kwargs) Bases: argparse.Action Read a configuration file and add it to the arguments to be parsed. The file should contain one “long option” per line, with or without the leading option characters (usually –), and otherwise entered as on the command-line with shell-style quoting. Lines beginning with a hash (#) or semicolon (;) and empty lines are ignored. Hashes (#) may also be used to comment option lines, excluding everything from the comment character to the end of the line. Key-value options may be separated by =, : or whitespace. By default, INI-style section markers are ignored. If, however, a program wants only to use settings in certain sections, it may pass a list of sections to allow. All other sections will be ignored. Section names are case- sensitive. The special None section will include global values which appear in the file before and section is declared. Options which set their config flag to False will not be allowed in configuration files. A default value can be set on the parser with the allow_in_config option. If ignore_unknown is True, lines containing an unknown option key will be ignored. Otherwise, an error will be issued (default behavior). Options read from the configuration file will normally be added before those for environment variables and other command-line arguments and in the order encountered to maintain the normal order of precedence. If inline is True, the configuration options will be inserted in place of the configuration option and will be processed after environment variables. itersettings(parser, conffile)

336 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

class volttron.platform.config.DebugArgumentParser(*args, **kwargs) Bases: volttron.platform.config.TrackingArgumentParser Write options to stderr as they are added to the namespace. This includes the source of the change. It does not include defaults. post_action(action, parser, namespace, values, option_string, source) class volttron.platform.config.ListAction(option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, re- quired=False, help=None, metavar=None) Bases: argparse.Action Action to store space or comma separated lists. split(value) class volttron.platform.config.SubParsersAction(option_strings, prog, parser_class, dest=’==SUPPRESS==’, help=None, metavar=None) Bases: argparse._SubParsersAction class volttron.platform.config.TrackingArgumentParser(*args, **kwargs) Bases: volttron.platform.config.ArgumentParser Wrap up calls to actions to pre- and post-handlers. This class is useful as a base class for debugging parsers. post_action(action, parser, namespace, values, option_string, source) pre_action(action, parser, namespace, values, option_string, source) class volttron.platform.config.TrackingString Bases: str String subclass that allows attaching source information. volttron.platform.config.env_var_formatter(formatter_class=) Decorator to automatically add env_var documentation to help. volttron.platform.config.expandall(string) volttron.platform.config.prompt_response(inputs) Prompt the user for answers. The inputs argument is a list or tuple with the following elements: [0] - The prompt to the user [1] - (Optional) A valid selection of responses [2] - (Optional) Default value if the user just types enter. volttron.platform.control module class volttron.platform.control.Agent(name, tag, uuid) Bases: tuple name Alias for field number 0 tag Alias for field number 1 uuid Alias for field number 2

10.4. Volttron 337 VOLTTRON Documentation, Release 3.5RC1 class volttron.platform.control.Connection(address, peer=’control’, publickey=None, se- cretkey=None, serverkey=None) Bases: object call(method, *args, **kwargs) kill(*args, **kwargs) notify(method, *args, **kwargs) server class volttron.platform.control.ControlService(aip, *args, **kwargs) Bases: volttron.platform.vip.agent.Agent agent_status(uuid) agent_vip_identity(uuid) Lookup the agent’s vip identity based upon it’s uuid. @param uuid: @return: clear_status(clear_all=False) install_agent(filename, channel_name) Installs an agent on the instance instance. The installation of an agent through this method involves sending the binary data of the agent file through a channel. The following example is the protocol for sending the agent across the wire: Example Protocol: # client creates channel to this agent (control) channel = agent.vip.channel(‘control’, ‘chan- nel_name’) # client waits for a ready response from control note this will # block until the repsonse is received. response = channel.recv() # Begin sending data while True: wheeldata = fin.read(8125) if not wheeldata: break channel.send(wheeldata) # send the done message channel.send(‘done’) # close and delete the channel chan- nel.close(linger=0) del channel

@param:string:filename: The name of the agent packaged file that is being written. @param:string:channel_name: The name of the channel that the agent file will be sent on.

list_agents() prioritize_agent(uuid, priority=‘50’) remove_agent(uuid) restart_agent(uuid) shutdown() start_agent(uuid) status_agents() stop_agent(uuid) stop_platform() tag_agent(uuid, tag)

338 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

volttron.platform.control.add_auth(opts) volttron.platform.control.add_server_key(opts) volttron.platform.control.clear_status(opts) volttron.platform.control.create_cgroups(opts) volttron.platform.control.disable_agent(opts) volttron.platform.control.do_stats(opts) volttron.platform.control.enable_agent(opts) volttron.platform.control.escape(pattern) volttron.platform.control.filter_agent(agents, pattern, opts) volttron.platform.control.filter_agents(agents, patterns, opts) volttron.platform.control.gen_keypair(opts) volttron.platform.control.get_keys(opts) Gets keys from keystore and known-hosts store volttron.platform.control.install_agent(opts) volttron.platform.control.list_agents(opts) volttron.platform.control.list_auth(opts, indices=None) volttron.platform.control.log_to_file(file, level=30, handler_class=) Direct log output to a file (or something like one). volttron.platform.control.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) volttron.platform.control.priority(value) volttron.platform.control.remove_agent(opts) volttron.platform.control.remove_auth(opts) volttron.platform.control.restart_agent(opts) volttron.platform.control.run_agent(opts) volttron.platform.control.send_agent(opts) volttron.platform.control.shutdown_agents(opts) volttron.platform.control.start_agent(opts) volttron.platform.control.status_agents(opts) volttron.platform.control.stop_agent(opts) volttron.platform.control.tag_agent(opts) volttron.platform.control.update_auth(opts) volttron.platform.jsonrpc module Implementation of JSON-RPC 2.0 with support for bi-directional calls. See http://www.jsonrpc.org/specification for the complete specification.

10.4. Volttron 339 VOLTTRON Documentation, Release 3.5RC1 exception volttron.platform.jsonrpc.Error(code, message, data=None) Bases: exceptions.Exception Raised when a recoverable JSON-RPC protocol error occurs. exception volttron.platform.jsonrpc.MethodNotFound(code, message, data=None) Bases: volttron.platform.jsonrpc.Error Raised when remote method is not implemented. exception volttron.platform.jsonrpc.RemoteError(message, **exc_info) Bases: exceptions.Exception Report the details of an error which occurred remotely. Instances of this exception are usually created by exception_from_json(), which uses the ‘detail’ element of the JSON-RPC error for message, if it is set, otherwise the JSON-RPC error message. The exc_info argument is set from the ‘exception.py’ element associated with an error code of -32000 (UNHANDLED_EXCEPTION). Typical keys in exc_info are exc_type, exc_args, and exc_tb (if tracebacks are allowed) which are stringified versions of the tuple returned from sys.exc_info(). print_tb(file=’, mode ‘w’>) Pretty print the traceback in the standard format. class volttron.platform.jsonrpc.Dispatcher Bases: object Parses and directs JSON-RPC 2.0 requests/responses. Parses a JSON-RPC message conatained in a dictionary (JavaScript object) or a batch of messages (list of dictionaries) and dispatches them appropriately. Subclasses must implement the serialize and deserialize methods with the JSON library of choice. The excep- tion, result, error, method and batch handling methods should also be implemented. batch(*args, **kwds) Context manager for batch requests. Entered before processing a batch request and exited afterward. batch_call(requests) Create and return a request for a batch of method calls. requests is an iterator of lists or tuples with 4 items each: ident, method, args, kwargs. These are the same 4 arguments required by the call() method. The first (ident) element may be None to indicate a notification. call(ident, method, args=None, kwargs=None) Create and return a request for a single method call. deserialize(json_string) Unpack a JSON string and return Python object(s). dispatch(json_string, context=None) Dispatch a JSON-RPC message and return a response or None. error(response, ident, code, message, data=None, context=None) Called when an error resposne is received. exception(response, ident, message, context=None) Called for response errors. Typically called when a response, such as an error, does not contain all the necessary members and sending an error to the remote peer is not possible. Also called when serializing a response fails.

340 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

method(request, ident, name, args, kwargs, batch=None, context=None) Called to get make method call and return results. request is the original JSON request (as dict). name is the name of the method requested. Only one of args or kwargs will contain parameters. If method is being executed as part of a batch request, batch will be the value returned from the batch() context manager. This method should raise NotImplementedError() if the method is unimplemented. Otherwise, it should return the result of the method call or raise an exception. If the raised exception has a traceback attribute, which should be a string (if set), it will be sent back in the returned error. An exc_info attribute may also be set which must be a dictionary and will be used as the basis for the exception.py member of the returned error. notify(method, args=None, kwargs=None) Create and return a request for a single notification. result(response, ident, result, context=None) Called when a result response is received. serialize(json_obj) Pack compatible Python objects into and return JSON string. volttron.platform.keystore module Module for storing local public and secret keys and remote public keys class volttron.platform.keystore.BaseJSONStore(filename, permissions=432) Bases: object JSON-file-backed store for dictionaries load() store(data) update(new_data) class volttron.platform.keystore.KeyStore(filename=None) Bases: volttron.platform.keystore.BaseJSONStore Handle generation, storage, and retrival of keys generate() isvalid() public() secret() class volttron.platform.keystore.KnownHostsStore(filename, permissions=432) Bases: volttron.platform.keystore.BaseJSONStore Handle storage and retrival of known hosts add(addr, server_key) serverkey(addr) volttron.platform.main module class volttron.platform.main.FramesFormatter(frames) Bases: object

10.4. Volttron 341 VOLTTRON Documentation, Release 3.5RC1

class volttron.platform.main.LogLevelAction(option_strings, dest, nargs=None, const=None, default=None, type=None, choices=None, re- quired=False, help=None, metavar=None) Bases: argparse.Action Action to set the log level of individual modules. class volttron.platform.main.Monitor(sock) Bases: threading.Thread Monitor thread to log connections. run() class volttron.platform.main.PubSubService(protected_topics_file, *args, **kwargs) Bases: volttron.platform.vip.agent.Agent setup_agent(sender, **kwargs) class volttron.platform.main.Router(local_address, addresses=(), context=None, se- cretkey=None, default_user_id=None, monitor=False, tracker=None) Bases: volttron.platform.vip.router.BaseRouter Concrete VIP router. handle_subsystem(frames, user_id) issue(topic, frames, extra=None) setup() volttron.platform.main.configure_logging(conf_path) Load logging configuration from a file. Several formats are possible: ini, JSON, Python, and YAML. The ini format uses the standard Windows ini file format and is read in using logging.config.fileConfig(). The remaining formats will be read in according to the serialization format and the resulting dictionary will be passed to logging.config.dictConfig(). See the logging.config module for specifics on the two file and dict formats. Returns None on success, (path, exception) on error. The default format is ini. Other formats will be selected based on the file extension. Each format can be forced, regardless of file extension, by prepending the path with the format name followed by a colon: Examples: config.json is loaded as JSON config.conf is loaded as ini json:config.conf is loaded as JSON YAML formatted configuration files require the PyYAML package. volttron.platform.main.log_to_file(file_, level=30, handler_class=) Direct log output to a file (or something like one). volttron.platform.main.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) volttron.platform.main.start_volttron_process(opts) Start the main volttron process. Typically this function is used from main.py and just uses the argparser’s Options arguments as inputs. It also can be called with a dictionary. In that case the dictionaries keys are mapped into a value that acts like the args options.

342 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1 volttron.platform.packages module class volttron.platform.packages.BasePackageVerifier(dist_info, **kwargs) Bases: object Base class for implementing wheel package verification. Verifies wheel packages as defined in PEP-427. May be inherited with minimal modifications to support differ- ent storage mechanisms, such as a filesystem, Zip file, or tarball. All paths are expected to be POSIX-style with forward-slashes. Subclasses should implement listdir and open and may override __init__, if needed. As an extension of the original specification, multiple levels of RECORD files and signatures are supported by appending incrementing integers to the RECORD files. Verification happens in reverse order and later RECORD files should contain hashes of the previous RECORD and associated signature file(s). get_records() Return a reverse sorted list of RECORD names from the package. Returns all RECORD files in the dist_info directory. iter_hashes(name=’RECORD’) Iterate over the files and hashes of a RECORD file. The RECORD file with the given name will be iterated over yielding a three tuple with each iteration: filename (relative to the package), computed hash (just calculated), and expected hash (from RECORD file). listdir(path) Return a possibly empty list of files from a directory. This could return the contents of a directory in an archive or whatever makes sense for the storage mech- anism. Paths will typically be relative to the package, however, for installed packages, absolute paths are possible. open(path, mode=’r’) Return a file-like object for the given file. mode is interpreted the same as for the built-in open and will be either ‘r’ or ‘rb’. Only the __iter__(), read(), and close() methods are used. class volttron.platform.packages.VolttronPackageWheelFileNoSign(filename, **kwargs) Bases: wheel.install.WheelFile AGENT_DATA_ZIP = ‘agent_data.zip’ add_files(files_to_add=None, basedir=’.’) contains(path) Does the wheel contain the specified path? pop_record_and_files() Pop off the last record file and files listed in it. Only removes files that are not listed in remaining records. pop_records_file() Pop off the last records file that was added remove_files(files) Relative to files in the package, ie: ./dist-info/config. unpack(dest=’.’) class volttron.platform.packages.ZipPackageVerifier(zip_path, mode=’r’, **kwargs) Bases: volttron.platform.packages.BasePackageVerifier

10.4. Volttron 343 VOLTTRON Documentation, Release 3.5RC1

Verify files of a Zip file. listdir(path) open(path, mode=’r’) class volttron.platform.packages.UnpackedPackage(base_directory) Bases: object Represents an package unpacked into a directory. Allows one access to the package metadata and methods to repack. metadata Parse package.dist-info/metadata.json and return a dictionary. package_name repack(dest=None, exclude=None) Recreate the package from the RECORD files. Put the package in the directory given by dest or in the current directory if dest is None. If exclude is given, do not add files for RECORD files in exclude. Returns the path to the new package. wheel_name wheelmeta Parse package.dist-info/WHEEL and return a dictionary.

volttron.platform.packaging module Agent packaging and signing support. exception volttron.platform.packaging.AgentPackageError Bases: exceptions.Exception Raised for errors during packaging, extraction and signing. volttron.platform.packaging.add_files_to_package(package, files=None) volttron.platform.packaging.create_package(agent_package_dir, wheelhouse) Creates a packaged whl file from the passed agent_package_dir. If the passed directory doesn’t exist or there isn’t a setup.py file the directory then AgentPackageError is raised. Parameters agent_package_dir - The directory to package in the wheel file. signature - An optional signature file to sign the RECORD file. Returns string - The full path to the created whl file. volttron.platform.packaging.extract_package(wheel_file, install_dir, include_uuid=False, specific_uuid=None) Extract a wheel file to the specified location. If include_uuid is True then a uuid will be generated under the passed location directory. The agent final directory will be based upon the wheel’s data directory name in the following formats: if include_uuid == True install_dir/datadir_name/uuid else install_dir/datadir_name

Arguments wheel_file - The wheel file to extract. install_dir - The root directory where to extract the wheel include_uuid - Auto-generates a uuuid under install_dir to place the wheel file data specific_uuid - A specific uuid to use for extracting the agent.

344 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

Returns The folder where the wheel was extracted.

volttron.platform.packaging.log_to_file(file, level=30, handler_class=) Direct log output to a file (or something like one). volttron.platform.packaging.main(argv=[’/home/docs/checkouts/readthedocs.org/user_builds/volttron/envs/new- logo/bin/sphinx-build’, ‘-b’, ‘latex’, ‘-D’, ‘language=en’, ‘-d’, ‘_build/doctrees’, ‘.’, ‘_build/latex’]) volttron.platform.packaging.repackage(directory, dest=None) Repack an wheel unpacked into the given directory. All files in the RECORD files are added back to the wheel, which is written in the current working directory if dest is None or in the directory given by dest otherwise.

volttron.platform.resmon module Resource Monitor The resource monitor manages resources assigned to the platform, assigns resources to agent execution environments, and monitors those resources for abuse. There will typically be only a single resource monitor that is instantiated and then set using set_resource_monitor(). Other modules may then just import the module and call the module-level functions without worrying about where to find the monitor instance. exception volttron.platform.resmon.ResourceError Bases: exceptions.Exception Exception raised for errors relating to this module. class volttron.platform.resmon.ExecutionEnvironment Bases: object Environment reserved for agent execution. Deleting ExecutionEnvironment objects should cause the process to end and all resources to be returned to the system. execute(*args, **kwargs) class volttron.platform.resmon.ResourceMonitor(env, **kwargs) Bases: object check_hard_resources(contract) Test contract against hard resources and return failed terms. contract should be a dictionary of terms and conditions that are being requested. If all terms can be met, None is returned. Otherwise, a dictionary is returned with the terms that failed along with hints on values that would cause the terms to succeed, if any. The contract is tested against the platform’s hard capabilities and static resources. get_static_resources(query_items=None) Return a dictionary of hard capabilities and static resources. query_items is a list of resources the requester is interested in; only items in the list will appear in the returned dictionary. If query_items is not passed or is None, all items should be returned. The returned dictionary contains the requested items that are available and their associated values and/or limits. Examples of static resources: architecture kernel version distribution (lsb_release) installed software

10.4. Volttron 345 VOLTTRON Documentation, Release 3.5RC1

reserve_soft_resources(contract) Test contract against soft resources and reserve resources. contract should be a dictionary of terms and conditions to test against the platform’s soft capabilities and dynamic resources. A 2-tuple is returned: (reservation, failed_terms). If reservation is None, no resources were reserved and failed_terms is a dictionary that can be consulted for the terms that must be modified for a reservation to succeed. Otherwise, reservation will be a ExecutionEnvironment object that can later be used to execute an agent and failed_terms will be None. volttron.platform.web module exception volttron.platform.web.CouldNotRegister Bases: exceptions.StandardError exception volttron.platform.web.DiscoveryError Bases: exceptions.StandardError Raised when a different volttron central tries to register. class volttron.platform.web.DiscoveryInfo(**kwargs) Bases: object A DiscoveryInfo class. The DiscoveryInfo class provides a wrapper around the return values from a call to the /discovery/ endpoint of the ‘volttron.platform.web. static request_discovery_info(discovery_address) Construct a DiscoveryInfo object. Requests a response from discovery_address and constructs a DiscoveryInfo object with the returned json. Parameters discovery_address – An http(s) address with volttron running. Returns class volttron.platform.web.MasterWebService(serverkey, identity, address, bind_web_address, aip, volt- tron_central_address=None) Bases: volttron.platform.vip.agent.Agent The service that is responsible for managing and serving registered pages Agents can register either a directory of files to serve or an rpc method that will be called during the request process. app_routing(env, start_response) The main routing function that maps the incoming request to a response. Depending on the registered routes map the request data onto an rpc function or a specific named file. get_bind_web_address() get_serverkey() get_volttron_central_address() Return address of external Volttron Central Note: this only applies to Volltron Central agents that are running on a different platform. register_agent_route(regex, peer, fn) Register an agent route to an exported function.

346 Chapter 10. API Documentation VOLTTRON Documentation, Release 3.5RC1

When a http request is executed and matches the passed regular expression then the function on peer is executed. register_path_route(peer, regex, root_dir) startupagent(sender, **kwargs) unregister_all_agent_routes(peer) volttron.platform.web.build_vip_address_string(vip_root, serverkey, publickey, se- cretkey) Build a full vip address string based upon the passed arguments All arguments are required to be non-None in order for the string to be created successfully. :raises ValueError if one of the parameters is None. volttron.platform.web.is_ip_private(vip_address) Determines if the passed vip_address is a private ip address or not. Parameters vip_address – A valid ip address. Returns True if an internal ip address.

Module contents Core package. volttron.platform.get_home() Return the home directory with user and variables expanded. If the VOLTTRON_HOME environment variable is set, it used. Otherwise, the default value of ‘~/.volttron’ is used. volttron.utils package

Submodules volttron.utils.persistance module class volttron.utils.persistance.PersistentDict(filename, flag=’c’, mode=None, for- mat=’pickle’, *args, **kwds) Bases: dict Persistent dictionary with an API compatible with shelve and anydbm. The dict is kept in memory, so the dictionary operations run as fast as a regular dictionary. Write to disk is delayed until close or sync (similar to gdbm’s fast mode). Input file format is automatically discovered. Output file format is selectable between pickle, json, and csv. All three serialization formats are backed by fast C implementations. close() dump(fileobj) load(fileobj) sync() Write dict to disk volttron.utils.persistance.load_create_store(filename)

Module contents

10.4. Volttron 347 VOLTTRON Documentation, Release 3.5RC1

Module contents

348 Chapter 10. API Documentation CHAPTER 11

Support

The easiest way to get help with your VOLTTRON installation is to join our office hours. The office hours are every other Friday at 11am PT. The office hours are available via Microsoft Lync. If you would like to be added to the VOLTTRON mailing list in order to be reminded of the meetings send an email to [email protected].

349 VOLTTRON Documentation, Release 3.5RC1

350 Chapter 11. Support CHAPTER 12

Indices and tables

• genindex • modindex • search

351 VOLTTRON Documentation, Release 3.5RC1

352 Chapter 12. Indices and tables Python Module Index

a drivenmatlab.drivenagent_pubsub, 276 actuator, 232 drivenmatlab.matlab, 276 actuator.agent, 221 actuator.scheduler, 230 e afdd, 261 economizer, 275 afdd.afdd, 260 economizer.copy_of_drivenagent, 272 afdd.afddagent, 261 economizer.drivenagent, 272 airside, 269 economizer.economizer_rcx, 272 airside.airside_retuning_rcx, 266 emailer, 235 airside.diagnostics, 266 emailer.agent, 234 airside.diagnostics.common, 261 eventperformance, 276 airside.diagnostics.reset_sched_rcx, 262 eventperformance.agent, 275 airside.diagnostics.satemp_rcx, 264 example, 289 airside.diagnostics.stcpr_rcx, 265 example.drivenagent, 288 airside.drivenagent, 268 example.drivencontrol, 288 alerter, 286 alerter.agent, 285 f alertmonitor, 285 forwarder, 235 askbot, 287 forwarder.agent, 235 askbot.agent, 286 g b greeter, 291 bacnet_proxy, 234 greeter.agent, 290 bacnet_proxy.agent, 233 greeter.settings, 291 baseline, 269 baseline.agent, 269 i ilc, 280 c ilc.agent, 277 c_agent, 287 ilc.ilc_matrices, 279 cumulativesum, 270 cumulativesum.agent, 269 l launcher, 253 d launcher.agent, 253 datapublisher, 288 launcher.settings, 253 datapublisher.publisher, 287 listener, 290 datapublisher.publisher3, 287 listener.agent, 289 DemandResponse, 270 listener.settings, 290 DemandResponse.dragent, 270 drivenmatlab, 277 m drivenmatlab.drivenagent, 276 master_driver, 247

353 VOLTTRON Documentation, Release 3.5RC1 master_driver.agent, 246 smaphistorian.historian, 259 master_driver.driver, 246 smaphistorian.settings, 260 master_driver.driver_exceptions, 247 smds, 284 master_driver.driver_locks, 247 smds.agent, 283 master_driver.interfaces, 240 smds.settings, 284 master_driver.interfaces.bacnet, 235 smdspush, 285 master_driver.interfaces.fakedriver, 236 smdspush.agent, 284 master_driver.interfaces.modbus, 236 sqlhistorian, 251 master_driver.interfaces.radiothermostatsqlhistorian.db, , 250 237 sqlhistorian.db.basedb, 249 master_driver.interfaces.thermostat_api, sqlhistorian.db.mysqlfuncts, 249 238 sqlhistorian.db.sqlitefuncts, 250 master_driver.tests, 245 sqlhistorian.historian, 250 master_driver.tests.test_revert_mixin, sqlhistorian.settings, 251 245 standalonelistener, 293 matlab_proxy, 281 subscriber, 289 matlab_proxy.agent, 280 subscriber.settings, 289 mobileexample, 290 subscriber.subscriber_agent, 289 mobileexample.agent, 290 sysmon, 252 mongodb, 247 sysmon.agent, 251 mongodb.historian, 247 mpc, 283 t mpc.agent, 281 tests, 233 mpc.CBC_Gui, 281 tests.test_actuator_pubsub, 232 mpc.MPC, 281 tests.test_actuator_rpc, 233 mpc.python_building, 282 tests.test_scheduler, 233 mpc.python_control, 282 thresholddetection, 253 multibuilding, 248 thresholddetection.agent, 252 multibuilding.agent, 247 v o vcplatform, 259 openadr, 283 vcplatform.agent, 257 openadr.agent, 283 volttron, 348 openeis, 249 volttron.drivers, 300 openeis.historian, 248 volttron.drivers.bacnet, 294 openeis.settings, 249 volttron.drivers.base, 296 volttron.drivers.data_logger, 298 p volttron.drivers.file_driver, 298 passiveafdd, 283 volttron.drivers.modbus, 299 passiveafdd.passive_afdd, 283 volttron.platform, 347 pingpong, 291 volttron.platform.agent, 314 pingpong.agent, 291 volttron.platform.agent.base, 301 volttron.platform.agent.base_historian, r 303 rpc_test_client, 253 volttron.platform.agent.cron, 309 volttron.platform.agent.driven, 309 s volttron.platform.agent.green, 310 schedule_example, 292 volttron.platform.agent.known_identities, schedule_example.agent, 292 310 schedule_example.settings, 292 volttron.platform.agent.matching, 311 settings, 293 volttron.platform.agent.math_utils, 312 simpleforwarder, 292 volttron.platform.agent.multithreading, simpleforwarder.simpleforwarder, 292 312 smaphistorian, 260 volttron.platform.agent.sched, 313

354 Python Module Index VOLTTRON Documentation, Release 3.5RC1 volttron.platform.agent.utils, 314 volttron.platform.vip.agent.subsystems.rpc, volttron.platform.aip, 330 321 volttron.platform.async, 332 volttron.platform.vip.agent.utils, 327 volttron.platform.auth, 332 volttron.platform.vip.green, 328 volttron.platform.config, 335 volttron.platform.vip.router, 328 volttron.platform.control, 337 volttron.platform.vip.socket, 329 volttron.platform.jsonrpc, 339 volttron.platform.vip.tracking, 330 volttron.platform.keystore, 341 volttron.platform.web, 346 volttron.platform.lib, 315 volttron.utils, 347 volttron.platform.lib.inotify, 314 volttron.utils.persistance, 347 volttron.platform.lib.inotify.green, 314 volttroncentral, 257 volttron.platform.lib.kwonlyargs, 314 volttroncentral.agent, 254 volttron.platform.lib.prctl, 315 volttroncentral.authenticate, 255 volttron.platform.main, 341 volttroncentral.registry, 255 volttron.platform.messaging, 318 volttroncentral.resource_directory, 257 volttron.platform.messaging.headers, 315 volttroncentral.sessions, 257 volttron.platform.messaging.health, 315 volttron.platform.messaging.socket, 316 w volttron.platform.messaging.topics, 317 weather, 259 volttron.platform.messaging.utils, 318 weather.settings, 259 volttron.platform.packages, 343 weathertest, 259 volttron.platform.packaging, 344 volttron.platform.resmon, 345 volttron.platform.vip, 330 volttron.platform.vip.agent, 328 volttron.platform.vip.agent.compat, 324 volttron.platform.vip.agent.core, 325 volttron.platform.vip.agent.decorators, 326 volttron.platform.vip.agent.dispatch, 326 volttron.platform.vip.agent.errors, 326 volttron.platform.vip.agent.example, 327 volttron.platform.vip.agent.results, 327 volttron.platform.vip.agent.subsystems, 322 volttron.platform.vip.agent.subsystems.base, 318 volttron.platform.vip.agent.subsystems.channel, 319 volttron.platform.vip.agent.subsystems.health, 319 volttron.platform.vip.agent.subsystems.heartbeat, 319 volttron.platform.vip.agent.subsystems.hello, 320 volttron.platform.vip.agent.subsystems.peerlist, 320 volttron.platform.vip.agent.subsystems.ping, 320 volttron.platform.vip.agent.subsystems.pubsub, 321 volttron.platform.vip.agent.subsystems.query, 321

Python Module Index 355 VOLTTRON Documentation, Release 3.5RC1

356 Python Module Index Index

A method), 321 abs_diff_mat_dat() (afdd.afdd.AFDD method), 260 add_update_tag() (volttroncen- AbstractDrivenAgent (class in volt- tral.registry.PlatformRegistry method), 255 tron.platform.agent.driven), 309 add_version_argument() (volt- active_agents() (volttron.platform.aip.AIPplatform tron.platform.config.ArgumentParser method), method), 330 336 ACTUATE_MODEL (volt- AddConstAction (class in volttron.platform.config), 336 tron.drivers.base.InterfaceFloatActuator at- Address (class in volttron.platform.vip.socket), 329 tribute), 297 ADDRESS (volttron.platform.vip.agent.subsystems.Channel actuator (module), 232 attribute), 323 actuator.agent (module), 221 ADDRESS (volttron.platform.vip.agent.subsystems.channel.Channel actuator.scheduler (module), 230 attribute), 319 actuator_agent() (in module actuator.agent), 230 advance() (mpc.python_building.Building method), 282 ActuatorAgent (class in actuator.agent), 226 AFDD (class in afdd.afdd), 260 add() (ilc.agent.Criteria method), 278 afdd (module), 261 add() (volttron.platform.auth.AuthFile method), 333 afdd.afdd (module), 260 add() (volttron.platform.keystore.KnownHostsStore afdd.afddagent (module), 261 method), 341 afdd0() (afdd.afdd.AFDD method), 260 add_auth() (in module volttron.platform.control), 339 afdd1() (afdd.afdd.AFDD method), 260 add_bus() (volttron.platform.vip.agent.subsystems.PubSub afdd2() (afdd.afdd.AFDD method), 260 method), 322 afdd3() (afdd.afdd.AFDD method), 260 add_bus() (volttron.platform.vip.agent.subsystems.pubsub.PubSubafdd4() (afdd.afdd.AFDD method), 260 method), 321 afdd5() (afdd.afdd.AFDD method), 260 add_capabilities() (volttron.platform.auth.AuthEntry afdd6() (afdd.afdd.AFDD method), 260 method), 333 AFDDAgent() (in module afdd.afddagent), 261 add_device_cluster() (ilc.agent.Clusters method), 278 Again, 327 add_files() (volttron.platform.packages.VolttronPackageWheelFileNoSignAgent (class in volttron.platform.control), 337 method), 343 Agent (class in volttron.platform.vip.agent), 328 add_files_to_package() (in module volt- agent (volttroncentral.agent.Connected attribute), 254 tron.platform.packaging), 344 agent (volttroncentral.agent.ConnectedLocalPlatform at- add_help_argument() (volt- tribute), 254 tron.platform.config.ArgumentParser method), agent (volttroncentral.agent.ConnectedPlatform at- 336 tribute), 254 add_server_key() (in module volttron.platform.control), Agent.Subsystems (class in volttron.platform.vip.agent), 339 328 add_subscription() (volt- AGENT_DATA_ZIP (volt- tron.platform.vip.agent.subsystems.PubSub tron.platform.packages.VolttronPackageWheelFileNoSign method), 322 attribute), 343 add_subscription() (volt- agent_id (actuator.scheduler.DeviceState attribute), 230 tron.platform.vip.agent.subsystems.pubsub.PubSubagent_identity() (volttron.platform.aip.AIPplatform method), 331

357 VOLTTRON Documentation, Release 3.5RC1 agent_name() (volttron.platform.aip.AIPplatform as_json() (volttron.platform.messaging.health.Status method), 331 method), 316 agent_priority() (volttron.platform.aip.AIPplatform ask_input() (askbot.agent.AskAgent method), 286 method), 331 AskAgent (class in askbot.agent), 286 agent_status() (vcplatform.agent.VolttronCentralPlatform askbot (module), 287 method), 258 askbot.agent (module), 286 agent_status() (volttron.platform.aip.AIPplatform assertempty() (in module volt- method), 331 tron.platform.lib.kwonlyargs), 315 agent_status() (volttron.platform.control.ControlService AsyncCall (class in volttron.platform.async), 332 method), 338 Authenticate (class in volttroncentral.authenticate), 255 agent_tag() (volttron.platform.aip.AIPplatform method), authenticate() (volttron.platform.auth.AuthService 331 method), 334 agent_uuid_from_pid() (volt- authenticate() (volttroncentral.authenticate.Authenticate tron.platform.aip.AIPplatform method), method), 255 331 authenticate() (volttroncentral.sessions.SessionHandler agent_vip_identity() (volt- method), 257 tron.platform.control.ControlService method), AuthEntry (class in volttron.platform.auth), 332 338 AuthEntryInvalid, 333 AgentPackageError, 344 AuthException, 333 AIPplatform (class in volttron.platform.aip), 330 AuthFile (class in volttron.platform.auth), 333 airside (module), 269 AuthFileEntryAlreadyExists, 334 airside.airside_retuning_rcx (module), 266 AuthFileIndexError, 334 airside.diagnostics (module), 266 AuthService (class in volttron.platform.auth), 334 airside.diagnostics.common (module), 261 autostart() (volttron.platform.aip.AIPplatform method), airside.diagnostics.reset_sched_rcx (module), 262 331 airside.diagnostics.satemp_rcx (module), 264 airside.diagnostics.stcpr_rcx (module), 265 B airside.drivenagent (module), 268 backup_new_data() (volt- alert() (thresholddetection.agent.ThresholdDetectionAgent tron.platform.agent.base_historian.BackupDatabase method), 252 method), 305 alerter (module), 286 BackupDatabase (class in volt- alerter.agent (module), 285 tron.platform.agent.base_historian), 305 AlerterAgent (class in alerter.agent), 285 BACnet (class in volttron.drivers.bacnet), 295 alertmonitor (module), 285 BACnet_application (class in bacnet_proxy.agent), 234 allow() (volttron.platform.vip.agent.subsystems.RPC BACnet_application (class in volttron.drivers.bacnet), method), 322 295 allow() (volttron.platform.vip.agent.subsystems.rpc.RPC bacnet_proxy (module), 234 method), 321 bacnet_proxy.agent (module), 233 AlreadyManagedError, 257 bacnet_proxy_agent() (in module bacnet_proxy.agent), annotate() (in module volt- 234 tron.platform.vip.agent.decorators), 326 BACnetInterface (class in volttron.drivers.bacnet), 295 annotations() (in module volt- BACnetProxyAgent (class in bacnet_proxy.agent), 233 tron.platform.vip.agent.decorators), 326 BACnetRegister (class in volttron.drivers.bacnet), 295 app_routing() (volttron.platform.web.MasterWebService bar() (standalonewithauth.StandAloneWithAuth method), method), 346 294 Application (class in airside.airside_retuning_rcx), 267 BaseAgent (class in volttron.platform.agent.base), 301 Application (class in drivenmatlab.matlab), 271, 277 BaseCriterion (class in ilc.agent), 277 Application (class in economizer.economizer_rcx), 273 BaseHistorian (class in volt- Application (class in example.drivencontrol), 289 tron.platform.agent.base_historian), 305 args (volttron.platform.agent.sched.Event attribute), 313 BaseHistorianAgent (class in volt- ArgumentParser (class in volttron.platform.config), 336 tron.platform.agent.base_historian), 305 as_dict() (volttron.platform.messaging.health.Status BaseInterface (class in master_driver.interfaces), 241 method), 315 BaseInterface (class in volttron.drivers.base), 296 BaseJSONStore (class in volttron.platform.keystore), 341

358 Index VOLTTRON Documentation, Release 3.5RC1 baseline (module), 269 call() (volttron.platform.jsonrpc.Dispatcher method), 340 baseline.agent (module), 269 call() (volttron.platform.vip.agent.subsystems.RPC BaselineAgent (class in baseline.agent), 269 method), 322 BasePackageVerifier (class in volt- call() (volttron.platform.vip.agent.subsystems.rpc.RPC tron.platform.packages), 343 method), 321 BaseQueryHistorianAgent (class in volt- call_foo_and_bar() (standalonewith- tron.platform.agent.base_historian), 306 auth.StandAloneWithAuth method), 294 BaseRegister (class in master_driver.interfaces), 243 cancel() (volttron.platform.agent.sched.Event method), BaseRegister (class in volttron.drivers.base), 296 313 BaseRouter (class in volttron.platform.vip.green), 328 cancel_task() (actuator.scheduler.ScheduleManager BaseRouter (class in volttron.platform.vip.router), 328 method), 231 BaseSmapVolttron (class in volttron.drivers.base), 297 canceled (volttron.platform.agent.sched.Event attribute), BasicAgent (class in volttron.platform.vip.agent), 328 313 BasicCore (class in volttron.platform.vip.agent.core), 325 CannotConnectError, 257 BasicRevert (class in master_driver.interfaces), 243 CaseInsensitiveConfigFileAction() (in module volt- batch() (volttron.platform.jsonrpc.Dispatcher method), tron.platform.config), 336 340 CBC_Gui (class in mpc.CBC_Gui), 281 batch() (volttron.platform.vip.agent.subsystems.RPC change_state() (actuator.scheduler.Task method), 232 method), 322 change_state() (askbot.agent.AskAgent method), 286 batch() (volttron.platform.vip.agent.subsystems.rpc.RPC Channel (class in volt- method), 321 tron.platform.vip.agent.subsystems), 323 batch_call() (volttron.platform.jsonrpc.Dispatcher Channel (class in volt- method), 340 tron.platform.vip.agent.subsystems.channel), baz() (volttron.platform.vip.agent.example.ExampleAgent 319 method), 327 check_availability() (actuator.scheduler.Schedule bind() (volttron.platform.vip.socket.Address method), method), 231 329 check_can_preempt_other() (actuator.scheduler.Task block_for_sync() (in module volttron.drivers.bacnet), 296 method), 232 build() (volttron.platform.messaging.health.Status static check_condition() (ilc.agent.ConditionalCurtailment method), 316 method), 278 build_agent() (in module volt- check_date() (in module airside.diagnostics.common), tron.platform.vip.agent.utils), 327 262 build_entry() (volttroncentral.registry.PlatformRegistry check_hard_resources() (volt- static method), 256 tron.platform.resmon.ResourceMonitor build_ranges_map() (mas- method), 345 ter_driver.interfaces.modbus.Interface method), check_resources() (volttron.platform.aip.AIPplatform 236 method), 331 build_ranges_map() (volt- check_run_status() (in module air- tron.drivers.modbus.ModbusInterface method), side.diagnostics.common), 262 300 check_session() (volttroncentral.sessions.SessionHandler build_register_map() (mas- method), 257 ter_driver.interfaces.BaseInterface method), classmethod() (volttron.platform.vip.agent.decorators.dualmethod 241 method), 326 build_register_map() (volttron.drivers.base.BaseInterface clean_up() (afdd.afdd.AFDD method), 260 method), 296 cleanup() (mpc.MPC.MPC method), 281 build_score() (in module ilc.ilc_matrices), 279 cleanup() (mpc.python_building.Building method), 282 build_vip_address_string() (in module volt- cleanup() (mpc.python_control.Control method), 282 tron.platform.web), 347 clear_data() (economizer.economizer_rcx.EconCorrectlyOff Building (class in mpc.python_building), 282 method), 274 clear_data() (economizer.economizer_rcx.EconCorrectlyOn C method), 274 c_agent (module), 287 clear_data() (economizer.economizer_rcx.ExcessOA calc_column_sums() (in module ilc.ilc_matrices), 279 method), 274 call() (volttron.platform.control.Connection method), 338

Index 359 VOLTTRON Documentation, Release 3.5RC1 clear_data() (economizer.economizer_rcx.InsufficientOA confirmation() (volttron.drivers.bacnet.BACnet_application method), 275 method), 295 clear_data() (economizer.economizer_rcx.TempSensorDx connect() (volttron.platform.vip.agent.dispatch.Signal method), 275 method), 326 clear_dirty_point() (mas- connect() (volttron.platform.vip.socket.Address method), ter_driver.interfaces.RevertTracker method), 330 244 connect() (volttroncentral.agent.ConnectedPlatform clear_status() (in module volttron.platform.control), 339 method), 254 clear_status() (volttron.platform.aip.AIPplatform Connected (class in volttroncentral.agent), 254 method), 331 connected (volttron.platform.vip.agent.core.Core at- clear_status() (volttron.platform.control.ControlService tribute), 325 method), 338 ConnectedLocalPlatform (class in volttroncentral.agent), close() (volttron.utils.persistance.PersistentDict method), 254 347 ConnectedPlatform (class in volttroncentral.agent), 254 close_async_connection_later() (volt- Connection (class in smdspush.agent), 284 tron.drivers.modbus.ModbusInterface method), Connection (class in volttron.platform.control), 338 300 ConstantCriterion (class in ilc.agent), 278 closed (volttron.platform.agent.base.BaseAgent at- contains() (volttron.platform.packages.VolttronPackageWheelFileNoSign tribute), 301 method), 343 Clusters (class in ilc.agent), 278 contains_include_start() (actuator.scheduler.TimeSlice command() (volttron.platform.agent.driven.Results method), 232 method), 310 context (volttron.platform.messaging.health.Status command_damper() (afdd.afdd.AFDD method), 260 attribute), 316 command_error_handler() (afdd.afdd.AFDD method), Control (class in mpc.python_control), 282 260 ControlService (class in volttron.platform.control), 338 command_outdoor_air_temperature_bias() ConversionMapper (class in volt- (afdd.afdd.AFDD method), 260 tron.platform.agent.driven), 310 command_volttron_flag() (afdd.afdd.AFDD method), copy() (volttron.platform.messaging.headers.Headers 260 method), 315 commit() (sqlhistorian.db.basedb.DbDriver method), 249 copy() (volttron.platform.messaging.socket.Headers CompatPubSub (class in volt- method), 316 tron.platform.vip.agent.compat), 324 Core (class in volttron.platform.vip.agent.core), 325 ConditionalCurtailment (class in ilc.agent), 278 CouldNotRegister, 346 config_dir (volttron.platform.aip.AIPplatform attribute), counter() (in module volttron.platform.vip.agent.results), 331 327 ConfigFileAction (class in volttron.platform.config), 336 cpu_percent() (sysmon.agent.SysMonAgent method), configure() (master_driver.interfaces.bacnet.Interface 251 method), 235 create() (volttron.platform.vip.agent.subsystems.Channel configure() (master_driver.interfaces.BaseInterface method), 323 method), 241 create() (volttron.platform.vip.agent.subsystems.channel.Channel configure() (master_driver.interfaces.fakedriver.Interface method), 319 method), 236 create_cgroups() (in module volttron.platform.control), configure() (master_driver.interfaces.modbus.Interface 339 method), 236 create_package() (in module volt- configure() (master_driver.interfaces.radiothermostat.Interface tron.platform.packaging), 344 method), 237 create_table_key() (in module air- configure_logging() (in module volttron.platform.main), side.diagnostics.reset_sched_rcx), 263 342 create_table_key() (in module air- configure_publish_lock() (in module mas- side.diagnostics.satemp_rcx), 265 ter_driver.driver_locks), 247 create_table_key() (in module air- configure_socket_lock() (in module mas- side.diagnostics.stcpr_rcx), 266 ter_driver.driver_locks), 247 Criteria (class in ilc.agent), 278 confirmation() (bacnet_proxy.agent.BACnet_application cumulativesum (module), 270 method), 234 cumulativesum.agent (module), 269

360 Index VOLTTRON Documentation, Release 3.5RC1

CumulativeSumAgent (class in cumulativesum.agent), dispatch() (volttron.platform.jsonrpc.Dispatcher method), 269 340 CurtailmentManager (class in ilc.agent), 278 Dispatcher (class in volttron.platform.jsonrpc), 340 CurtailmentSetting (class in ilc.agent), 278 display_name (volttroncentral.registry.RegistryEntry at- tribute), 256 D do_rpc() (in module rpc_test_client), 253 data (actuator.scheduler.RequestResult attribute), 231 do_stats() (in module volttron.platform.control), 339 DataLogger (class in volttron.drivers.data_logger), 298 driven_agent() (in module airside.drivenagent), 268 DataPub() (in module datapublisher.publisher), 287 driven_agent() (in module drivenmatlab.drivenagent), DataPub() (in module datapublisher.publisher3), 287 270, 276 datapublisher (module), 288 driven_agent() (in module drivenmat- datapublisher.publisher (module), 287 lab.drivenagent_pubsub), 270, 276 datapublisher.publisher3 (module), 287 DrivenAgent() (in module econo- DatetimeFromValue() (in module sched- mizer.copy_of_drivenagent), 272 ule_example.agent), 292 DrivenAgent() (in module economizer.drivenagent), 272 DatetimeFromValue() (in module smdspush.agent), 284 DrivenAgent() (in module example.drivenagent), 288 DbDriver (class in sqlhistorian.db.basedb), 249 drivenmatlab (module), 272, 277 DebugArgumentParser (class in volttron.platform.config), drivenmatlab.drivenagent (module), 270, 276 336 drivenmatlab.drivenagent_pubsub (module), 270, 276 delay() (volttron.platform.agent.sched.Queue method), drivenmatlab.matlab (module), 270, 276 313 DriverAgent (class in master_driver.driver), 246 DemandResponse (module), 270 DriverConfigError, 247 DemandResponse.dragent (module), 270 DriverError, 247 DemandResponseAgent() (in module Deman- DriverInterfaceError, 244 dResponse.dragent), 270 drop_subscription() (volt- deserialize() (volttron.platform.jsonrpc.Dispatcher tron.platform.vip.agent.subsystems.PubSub method), 340 method), 322 Device (class in ilc.agent), 278 drop_subscription() (volt- device_startup_callback() (mas- tron.platform.vip.agent.subsystems.pubsub.PubSub ter_driver.agent.MasterDriverAgent method), method), 321 246 dualmethod (class in volt- DeviceCluster (class in ilc.agent), 279 tron.platform.vip.agent.decorators), 326 DeviceState (class in actuator.scheduler), 230 duct_static() (airside.diagnostics.stcpr_rcx.DuctStaticRcx dict (volttron.platform.messaging.headers.Headers method), 266 attribute), 315 DuctStaticRcx (class in airside.diagnostics.stcpr_rcx), dict (volttron.platform.messaging.socket.Headers at- 266 tribute), 317 dump() (volttron.utils.persistance.PersistentDict method), disable() (volttron.platform.vip.tracking.Tracker 347 method), 330 dump_user() (in module volttron.platform.auth), 335 disable_agent() (in module volttron.platform.control), DuplicateUUIDError, 255 339 disconnect() (volttron.platform.vip.agent.dispatch.Signal E method), 326 econ_alg1() (economizer.economizer_rcx.TempSensorDx disconnect() (volttroncen- method), 275 tral.agent.ConnectedLocalPlatform method), econ_alg2() (economizer.economizer_rcx.EconCorrectlyOn 254 method), 274 disconnect() (volttroncentral.agent.ConnectedPlatform econ_alg3() (economizer.economizer_rcx.EconCorrectlyOff method), 254 method), 274 discovery_address (volttroncentral.registry.RegistryEntry econ_alg4() (economizer.economizer_rcx.ExcessOA attribute), 256 method), 274 DiscoveryError, 346 econ_alg5() (economizer.economizer_rcx.InsufficientOA DiscoveryInfo (class in volttron.platform.web), 346 method), 275 disk_percent() (sysmon.agent.SysMonAgent method), EconCorrectlyOff (class in economizer.economizer_rcx), 251 273

Index 361 VOLTTRON Documentation, Release 3.5RC1

EconCorrectlyOn (class in economizer.economizer_rcx), exec_method() (in module rpc_test_client), 253 274 execute() (volttron.platform.agent.sched.Queue method), economizer (module), 275 313 economizer.copy_of_drivenagent (module), 272 execute() (volttron.platform.aip.ExecutionEnvironment economizer.drivenagent (module), 272 method), 332 economizer.economizer_rcx (module), 272 execute() (volttron.platform.resmon.ExecutionEnvironment economizing_when_not_needed() (econo- method), 345 mizer.economizer_rcx.EconCorrectlyOff ExecutionEnvironment (class in volttron.platform.aip), method), 274 331 EKGregister (class in mas- ExecutionEnvironment (class in volt- ter_driver.interfaces.fakedriver), 236 tron.platform.resmon), 345 emailer (module), 235 exit() (mpc.CBC_Gui.CBC_Gui method), 281 emailer.agent (module), 234 expandall() (in module volttron.platform.config), 337 EmailerAgent (class in emailer.agent), 234 export() (volttron.platform.vip.agent.subsystems.RPC enable() (volttron.platform.vip.tracking.Tracker method), method), 322 330 export() (volttron.platform.vip.agent.subsystems.rpc.RPC enable_agent() (in module volttron.platform.control), 339 method), 321 end (actuator.scheduler.TimeSlice attribute), 232 extract_criteria() (in module ilc.ilc_matrices), 280 energy_led() (master_driver.interfaces.thermostat_api.ThermostatInterfaceextract_package() (in module volt- method), 238 tron.platform.packaging), 344 env_var_formatter() (in module volttron.platform.config), 337 F Error, 339 FakeRegister (class in mas- error() (volttron.platform.jsonrpc.Dispatcher method), ter_driver.interfaces.fakedriver), 236 340 File (class in volttron.drivers.file_driver), 298 escape() (in module volttron.platform.control), 339 FileInterface (class in volttron.drivers.file_driver), 298 evaluate() (ilc.agent.BaseCriterion method), 277 FileRegister (class in volttron.drivers.file_driver), 299 evaluate() (ilc.agent.ConstantCriterion method), 278 filter_agent() (in module volttron.platform.control), 339 evaluate() (ilc.agent.Criteria method), 278 filter_agents() (in module volttron.platform.control), 339 evaluate() (ilc.agent.Device method), 278 find_by_credentials() (volttron.platform.auth.AuthFile evaluate() (ilc.agent.FormulaCriterion method), 279 method), 333 evaluate() (ilc.agent.HistoryCriterion method), 279 find_registration_address() (in module vcplatform.agent), evaluate() (ilc.agent.MapperCriterion method), 279 258 evaluate() (ilc.agent.StatusCriterion method), 279 finish() (launcher.agent.ProcessAgent method), 253, 291 evaluate_bounds() (ilc.agent.BaseCriterion method), 277 finish() (volttron.platform.agent.base.BaseAgent evaluate_criterion() (ilc.agent.BaseCriterion method), method), 301 278 finish() (volttron.platform.aip.AIPplatform method), 331 Event (class in volttron.platform.agent.sched), 313 finish() (volttron.platform.vip.agent.example.ExampleAgent eventperformance (module), 276 method), 327 eventperformance.agent (module), 275 finished (volttron.platform.agent.sched.Event attribute), EventPerformanceAgent (class in eventperfor- 313 mance.agent), 275 finished() (actuator.scheduler.Schedule method), 231 EventWithTime (class in volttron.platform.agent.sched), fmode() (master_driver.interfaces.thermostat_api.ThermostatInterface 313 method), 238 example (module), 289 foo() (standalonewithauth.StandAloneWithAuth method), example.drivenagent (module), 288 294 example.drivencontrol (module), 288 foo() (volttron.platform.vip.agent.example.ExampleAgent ExampleAgent (class in volt- method), 327 tron.platform.vip.agent.example), 327 format() (volttron.platform.messaging.utils.Topic exception() (volttron.platform.jsonrpc.Dispatcher method), 318 method), 340 FormulaCriterion (class in ilc.agent), 279 excess_oa() (economizer.economizer_rcx.ExcessOA forward() (volttron.platform.vip.agent.compat.CompatPubSub method), 274 method), 325 ExcessOA (class in economizer.economizer_rcx), 274 forwarder (module), 235

362 Index VOLTTRON Documentation, Release 3.5RC1 forwarder.agent (module), 235 get_device() (ilc.agent.Clusters method), 278 FramesFormatter (class in volttron.platform.main), 341 get_device_name_list() (ilc.agent.Clusters method), 278 from_errno() (volttron.platform.vip.agent.errors.VIPError get_devices() (vcplatform.agent.VolttronCentralPlatform class method), 326 method), 258 from_json() (volttron.platform.messaging.health.Status get_devices() (volttroncentral.registry.PlatformRegistry static method), 316 method), 256 function (volttron.platform.agent.sched.Event attribute), get_dict() (in module rpc_test_client), 253 313 get_execreqs() (volttron.platform.aip.AIPplatform method), 331 G get_groups() (volttron.platform.auth.AuthService gen_keypair() (in module volttron.platform.control), 339 method), 335 generate() (volttron.platform.keystore.KeyStore method), get_health() (vcplatform.agent.VolttronCentralPlatform 341 method), 258 get() (volttron.platform.messaging.headers.Headers get_heat_pgm() (master_driver.interfaces.thermostat_api.ThermostatInterface method), 315 method), 239 get() (volttron.platform.messaging.socket.Headers get_high_temp_limit() (mpc.python_building.Building method), 317 method), 282 get_agent_list() (volttroncen- get_home() (in module volttron.platform), 347 tral.registry.PlatformRegistry method), 256 get_hvac_command() (mpc.python_control.Control get_all_device_evaluations() (ilc.agent.DeviceCluster method), 282 method), 279 get_indoor_temp() (mpc.python_building.Building get_all_revert_values() (mas- method), 282 ter_driver.interfaces.RevertTracker method), get_interface() (master_driver.driver.DriverAgent 245 method), 246 get_authorizations() (volttron.platform.auth.AuthService get_interface() (volttron.drivers.bacnet.BACnet method), method), 334 295 get_bind_web_address() (volt- get_interface() (volttron.drivers.base.BaseSmapVolttron tron.platform.web.MasterWebService method), method), 297 346 get_interface() (volttron.drivers.file_driver.File method), get_capabilities() (volttron.platform.auth.AuthService 298 method), 335 get_interface() (volttron.drivers.modbus.Modbus get_config() (master_driver.driver.DriverAgent method), method), 299 246 get_keys() (in module volttron.platform.control), 339 get_conflicts() (actuator.scheduler.Schedule method), 231 get_low_temp_limit() (mpc.python_building.Building get_conflicts() (actuator.scheduler.Task method), 232 method), 282 get_control_period() (mpc.python_control.Control get_next_event_time() (actuator.scheduler.Schedule method), 282 method), 231 get_cool_pgm() (master_driver.interfaces.thermostat_api.ThermostatInterfaceget_next_event_time() (actua- method), 238 tor.scheduler.ScheduleManager method), get_current_slot() (actuator.scheduler.Schedule method), 231 231 get_next_event_time() (actuator.scheduler.Task method), get_current_slots() (actuator.scheduler.Task method), 232 232 get_curtailment() (ilc.agent.ConditionalCurtailment get_next_invoke_id() (bac- method), 278 net_proxy.agent.BACnet_application method), get_curtailment() (ilc.agent.Criteria method), 278 234 get_curtailment() (ilc.agent.CurtailmentManager get_next_invoke_id() (volt- method), 278 tron.drivers.bacnet.BACnet_application get_curtailment() (ilc.agent.Device method), 279 method), 295 get_curtailment_dict() (ilc.agent.CurtailmentSetting get_num_zones() (mpc.python_building.Building method), 278 method), 282 get_description() (master_driver.interfaces.BaseRegister get_on_commands() (ilc.agent.Device method), 279 method), 243 get_outdoor_temp() (mpc.MPC.MPC method), 281 get_description() (volttron.drivers.base.BaseRegister get_outdoor_temp() (mpc.python_building.Building method), 297 method), 282

Index 363 VOLTTRON Documentation, Release 3.5RC1 get_outstanding_to_publish() (volt- get_point_sync() (volttron.drivers.modbus.ModbusInterface tron.platform.agent.base_historian.BackupDatabase method), 300 method), 305 get_points() (smdspush.agent.Connection method), 284 get_paths_for_point() (master_driver.driver.DriverAgent get_publickey() (vcplat- method), 246 form.agent.VolttronCentralPlatform method), get_paths_for_point() (volt- 258 tron.drivers.base.BaseSmapVolttron method), get_records() (volttron.platform.packages.BasePackageVerifier 297 method), 343 get_performance() (volttroncen- get_register_by_name() (mas- tral.registry.PlatformRegistry method), 256 ter_driver.interfaces.BaseInterface method), get_platform() (volttroncen- 242 tral.agent.VolttronCentralAgent method), get_register_by_name() (volt- 254 tron.drivers.base.BaseInterface method), get_platform() (volttroncentral.registry.PlatformRegistry 296 method), 256 get_register_count() (mas- get_platform_by_address() (volttroncen- ter_driver.interfaces.modbus.ModbusBitRegister tral.registry.PlatformRegistry method), 256 method), 236 get_platforms() (volttroncen- get_register_count() (mas- tral.agent.VolttronCentralAgent method), ter_driver.interfaces.modbus.ModbusByteRegister 254 method), 237 get_platforms() (volttroncentral.registry.PlatformRegistry get_register_count() (volt- method), 256 tron.drivers.modbus.ModbusBitRegister get_point() (actuator.agent.ActuatorAgent method), 227 method), 299 get_point() (master_driver.agent.MasterDriverAgent get_register_count() (volt- method), 246 tron.drivers.modbus.ModbusByteRegister get_point() (master_driver.driver.DriverAgent method), method), 300 246 get_register_names() (mas- get_point() (master_driver.interfaces.bacnet.Interface ter_driver.interfaces.BaseInterface method), method), 235 242 get_point() (master_driver.interfaces.BaseInterface get_register_names() (volttron.drivers.base.BaseInterface method), 242 method), 296 get_point() (master_driver.interfaces.fakedriver.Interface get_register_python_type() (mas- method), 236 ter_driver.interfaces.BaseRegister method), get_point() (master_driver.interfaces.modbus.Interface 243 method), 236 get_register_python_type() (volt- get_point() (master_driver.interfaces.radiothermostat.Interface tron.drivers.base.BaseRegister method), method), 237 297 get_point_async() (volt- get_register_type() (mas- tron.drivers.bacnet.BACnetInterface method), ter_driver.interfaces.BaseRegister method), 295 243 get_point_async() (volttron.drivers.base.BaseInterface get_register_type() (volttron.drivers.base.BaseRegister method), 296 method), 297 get_point_async() (volt- get_registers_by_type() (mas- tron.drivers.file_driver.FileInterface method), ter_driver.interfaces.BaseInterface method), 298 242 get_point_async() (volt- get_registers_by_type() (volt- tron.drivers.modbus.ModbusInterface method), tron.drivers.base.BaseInterface method), 300 296 get_point_sync() (volttron.drivers.bacnet.BACnetInterface get_revert_value() (mas- method), 295 ter_driver.interfaces.RevertTracker method), get_point_sync() (volttron.drivers.base.BaseInterface 245 method), 296 get_roles() (volttron.platform.auth.AuthService method), get_point_sync() (volttron.drivers.file_driver.FileInterface 335 method), 299 get_rtu_status() (afdd.afdd.AFDD method), 261

364 Index VOLTTRON Documentation, Release 3.5RC1 get_schedule() (actuator.scheduler.Schedule method), get_topic_list() (volttron.platform.agent.base_historian.BaseQueryHistorianAgent 231 method), 306 get_schedule_state() (actua- get_topic_map() (sqlhistorian.db.basedb.DbDriver tor.scheduler.ScheduleManager method), method), 249 231 get_topic_map() (sqlhisto- get_score_order() (ilc.agent.Clusters method), 278 rian.db.mysqlfuncts.MySqlFuncts method), get_serverkey() (volttron.platform.web.MasterWebService 249 method), 346 get_topic_map() (sqlhisto- get_setting() (vcplatform.agent.VolttronCentralPlatform rian.db.sqlitefuncts.SqlLiteFuncts method), method), 258 250 get_state() (master_driver.interfaces.modbus.ModbusBitRegisterget_units() (master_driver.interfaces.BaseRegister method), 236 method), 243 get_state() (master_driver.interfaces.modbus.ModbusByteRegisterget_units() (volttron.drivers.base.BaseRegister method), method), 237 297 get_state() (volttron.drivers.base.InterfaceBitActuator get_value() (volttron.drivers.file_driver.FileRegister method), 297 method), 299 get_state() (volttron.drivers.base.InterfaceFloatActuator get_values() (smdspush.agent.Connection method), 284 method), 297 get_vip_addresses() (volttroncen- get_state() (volttron.drivers.base.InterfaceIntActuator tral.registry.PlatformRegistry method), 256 method), 297 get_volttron_central_address() (volt- get_state_async() (volt- tron.platform.web.MasterWebService method), tron.drivers.bacnet.BACnetRegister method), 346 295 goodbye() (volttron.platform.vip.agent.example.ExampleAgent get_state_async() (volt- method), 327 tron.drivers.modbus.ModbusBitRegister GreenletExit, 332 method), 299 greeter (module), 291 get_state_async() (volt- greeter.agent (module), 290 tron.drivers.modbus.ModbusByteRegister greeter.settings (module), 291 method), 300 GreeterAgent (class in greeter.agent), 290 get_state_callback() (volt- tron.drivers.modbus.ModbusBitRegister H method), 299 handle_accept() (askbot.agent.AskAgent method), 286 get_state_callback() (volt- handle_error() (volttron.platform.vip.agent.core.Core tron.drivers.modbus.ModbusByteRegister method), 325 method), 300 handle_get() (actuator.agent.ActuatorAgent method), 227 get_state_sync() (volttron.drivers.bacnet.BACnetRegister handle_input() (askbot.agent.AskAgent method), 286 method), 295 handle_request() (bacnet_proxy.agent.BACnet_application get_state_sync() (volttron.drivers.modbus.ModbusBitRegister method), 234 method), 299 handle_revert_device() (actuator.agent.ActuatorAgent get_state_sync() (volttron.drivers.modbus.ModbusByteRegister method), 227 method), 300 handle_revert_point() (actuator.agent.ActuatorAgent get_static_resources() (volt- method), 227 tron.platform.resmon.ResourceMonitor handle_schedule_request() (actua- method), 345 tor.agent.ActuatorAgent method), 228 get_status() (volttron.platform.vip.agent.subsystems.Health handle_set() (actuator.agent.ActuatorAgent method), 228 method), 324 handle_sub_message() (volt- get_status() (volttron.platform.vip.agent.subsystems.health.Health tron.platform.agent.base.BaseAgent method), method), 319 301 get_switch() (volttron.platform.config.ArgumentParser handle_subsystem() (volttron.platform.main.Router method), 336 method), 342 get_tag() (volttroncentral.registry.PlatformRegistry handle_subsystem() (volt- method), 256 tron.platform.vip.router.BaseRouter method), get_timeunit() (in module volt- 328 tron.platform.agent.base_historian), 308

Index 365 VOLTTRON Documentation, Release 3.5RC1

Headers (class in volttron.platform.messaging.headers), in_loop() (volttron.platform.vip.agent.compat.CompatPubSub 315 method), 325 Headers (class in volttron.platform.messaging.socket), increment_curtail() (ilc.agent.Criteria method), 278 316 increment_curtail() (ilc.agent.Device method), 279 Headers.Key (class in volt- info_string (actuator.scheduler.RequestResult attribute), tron.platform.messaging.headers), 315 231 Headers.Key (class in volt- ingest_data() (ilc.agent.BaseCriterion method), 278 tron.platform.messaging.socket), 316 ingest_data() (ilc.agent.ConditionalCurtailment method), Health (class in volttron.platform.vip.agent.subsystems), 278 323 ingest_data() (ilc.agent.Criteria method), 278 Health (class in volt- ingest_data() (ilc.agent.CurtailmentManager method), tron.platform.vip.agent.subsystems.health), 278 319 ingest_data() (ilc.agent.CurtailmentSetting method), 278 heart_beat() (master_driver.agent.MasterDriverAgent ingest_data() (ilc.agent.Device method), 279 method), 246 ingest_data() (ilc.agent.FormulaCriterion method), 279 heart_beat() (master_driver.driver.DriverAgent method), ingest_data() (ilc.agent.HistoryCriterion method), 279 246 ingest_data() (ilc.agent.StatusCriterion method), 279 Heartbeat (class in volt- init_algo_state() (afdd.afdd.AFDD method), 261 tron.platform.vip.agent.subsystems), 323 init_microsecond_support() (sqlhisto- Heartbeat (class in volt- rian.db.mysqlfuncts.MySqlFuncts method), tron.platform.vip.agent.subsystems.heartbeat), 249 319 inotify (class in volttron.platform.lib.inotify), 314 Hello (class in volttron.platform.vip.agent.subsystems), inotify (class in volttron.platform.lib.inotify.green), 314 322 input_matrix() (in module ilc.ilc_matrices), 280 Hello (class in volttron.platform.vip.agent.subsystems.hello),insert_data() (sqlhistorian.db.basedb.DbDriver method), 320 249 hello() (volttron.platform.vip.agent.example.ExampleAgent insert_data_query() (sqlhistorian.db.basedb.DbDriver method), 327 method), 249 hello() (volttron.platform.vip.agent.subsystems.Hello insert_data_query() (sqlhisto- method), 322 rian.db.mysqlfuncts.MySqlFuncts method), hello() (volttron.platform.vip.agent.subsystems.hello.Hello 249 method), 320 insert_data_query() (sqlhisto- high_sat() (airside.diagnostics.satemp_rcx.SupplyTempRcx rian.db.sqlitefuncts.SqlLiteFuncts method), method), 264 250 high_stcpr_dx() (airside.diagnostics.stcpr_rcx.DuctStaticRcxinsert_meta() (sqlhistorian.db.basedb.DbDriver method), method), 266 249 historian() (in module forwarder.agent), 235 insert_meta_query() (sqlhistorian.db.basedb.DbDriver historian() (in module mongodb.historian), 247 method), 249 historian() (in module openeis.historian), 248 insert_meta_query() (sqlhisto- historian() (in module sqlhistorian.historian), 250 rian.db.mysqlfuncts.MySqlFuncts method), historian_setup() (volttron.platform.agent.base_historian.BaseHistorianAgent249 method), 306 insert_meta_query() (sqlhisto- HistoryCriterion (class in ilc.agent), 279 rian.db.sqlitefuncts.SqlLiteFuncts method), hit() (volttron.platform.vip.tracking.Tracker method), 330 250 hold() (master_driver.interfaces.thermostat_api.ThermostatInterfaceinsert_register() (master_driver.interfaces.BaseInterface method), 239 method), 242 insert_register() (master_driver.interfaces.modbus.Interface I method), 236 ignore (volttron.platform.aip.IgnoreErrno attribute), 332 insert_register() (volttron.drivers.bacnet.BACnetInterface IgnoreErrno (class in volttron.platform.aip), 332 method), 295 ilc (module), 280 insert_register() (volttron.drivers.base.BaseInterface ilc.agent (module), 277 method), 296 ilc.ilc_matrices (module), 279 insert_register() (volttron.drivers.modbus.ModbusInterface ilc_agent() (in module ilc.agent), 279 method), 300

366 Index VOLTTRON Documentation, Release 3.5RC1 insert_table_row() (volt- is_managed() (vcplatform.agent.VolttronCentralPlatform tron.platform.agent.driven.Results method), method), 258 310 is_number() (in module volt- insert_topic() (sqlhistorian.db.basedb.DbDriver method), tron.platform.agent.base_historian), 308 249 isregex() (in module volttron.platform.auth), 335 insert_topic_query() (sqlhistorian.db.basedb.DbDriver issue() (volttron.platform.main.Router method), 342 method), 249 issue() (volttron.platform.vip.router.BaseRouter method), insert_topic_query() (sqlhisto- 329 rian.db.mysqlfuncts.MySqlFuncts method), isvalid() (volttron.platform.keystore.KeyStore method), 249 341 insert_topic_query() (sqlhisto- iter_hashes() (volttron.platform.packages.BasePackageVerifier rian.db.sqlitefuncts.SqlLiteFuncts method), method), 343 250 iter_match_tests() (in module volt- inspect_agent() (in module rpc_test_client), 253 tron.platform.agent.matching), 311 inspect_method() (in module rpc_test_client), 253 itersettings() (volttron.platform.config.ConfigFileAction install_agent() (in module volttron.platform.control), 339 method), 336 install_agent() (volttron.platform.aip.AIPplatform method), 331 J install_agent() (volttron.platform.control.ControlService jsonrpc() (volttroncentral.agent.VolttronCentralAgent method), 338 method), 254 install_dir (volttron.platform.aip.AIPplatform attribute), 331 K instancemethod() (volt- KeyStore (class in volttron.platform.keystore), 341 tron.platform.vip.agent.decorators.dualmethod kill() (volttron.platform.async.Threadlet method), 332 method), 326 kill() (volttron.platform.control.Connection method), 338 insufficient_oa() (econo- kill_all() (volttron.platform.agent.green.WaitQueue mizer.economizer_rcx.InsufficientOA method), method), 310 275 killing() (in module volttron.platform.vip.agent.core), InsufficientOA (class in economizer.economizer_rcx), 325 274 KnownHostsStore (class in volttron.platform.keystore), Interface (class in master_driver.interfaces.bacnet), 235 341 Interface (class in master_driver.interfaces.fakedriver), kwargs (volttron.platform.agent.sched.Event attribute), 236 313 Interface (class in master_driver.interfaces.modbus), 236 Interface (class in mas- L ter_driver.interfaces.radiothermostat), 237 land_agent() (volttron.platform.aip.AIPplatform method), InterfaceBitActuator (class in volttron.drivers.base), 297 331 InterfaceFloatActuator (class in volttron.drivers.base), last_updated (volttron.platform.messaging.health.Status 297 attribute), 316 InterfaceIntActuator (class in volttron.drivers.base), 297 launch_agent() (volttron.platform.aip.AIPplatform IOCB (class in bacnet_proxy.agent), 234 method), 331 IOCB (class in volttron.drivers.bacnet), 296 launcher (module), 253, 292 is_connected() (volttroncentral.agent.Connected launcher.agent (module), 253, 291 method), 254 launcher.settings (module), 253, 292 is_connected() (volttroncen- linear_interpolation() (ilc.agent.HistoryCriterion tral.agent.ConnectedLocalPlatform method), method), 279 254 List (class in volttron.platform.auth), 335 is_connected() (volttroncentral.agent.ConnectedPlatform list() (volttron.platform.vip.agent.subsystems.PeerList method), 254 method), 322 is_ip_private() (in module vcplatform.agent), 258 list() (volttron.platform.vip.agent.subsystems.peerlist.PeerList is_ip_private() (in module volttron.platform.web), 347 method), 320 is_local (volttroncentral.registry.RegistryEntry attribute), list() (volttron.platform.vip.agent.subsystems.PubSub 256 method), 322

Index 367 VOLTTRON Documentation, Release 3.5RC1 list() (volttron.platform.vip.agent.subsystems.pubsub.PubSubloop() (volttron.platform.vip.agent.core.Core method), method), 321 325 list_agent_methods() (vcplat- LOOP_INTERVAL (volt- form.agent.VolttronCentralPlatform method), tron.platform.agent.base.BaseAgent attribute), 258 301 list_agents() (in module rpc_test_client), 253 low_sat() (airside.diagnostics.satemp_rcx.SupplyTempRcx list_agents() (in module volttron.platform.control), 339 method), 265 list_agents() (vcplatform.agent.VolttronCentralPlatform low_stcpr_dx() (airside.diagnostics.stcpr_rcx.DuctStaticRcx method), 258 method), 266 list_agents() (volttron.platform.aip.AIPplatform method), 331 M list_agents() (volttron.platform.control.ControlService main() (in module actuator.agent), 230 method), 338 main() (in module afdd.afddagent), 261 list_auth() (in module volttron.platform.control), 339 main() (in module airside.drivenagent), 268 list_platform_details() (volttroncen- main() (in module alerter.agent), 285 tral.agent.VolttronCentralAgent method), main() (in module askbot.agent), 286 255 main() (in module bacnet_proxy.agent), 234 ListAction (class in volttron.platform.config), 337 main() (in module baseline.agent), 269 listdir() (volttron.platform.packages.BasePackageVerifier main() (in module cumulativesum.agent), 269 method), 343 main() (in module datapublisher.publisher), 287 listdir() (volttron.platform.packages.ZipPackageVerifier main() (in module datapublisher.publisher3), 287 method), 344 main() (in module DemandResponse.dragent), 270 listener (module), 290 main() (in module drivenmatlab.drivenagent), 270, 276 listener.agent (module), 289 main() (in module drivenmatlab.drivenagent_pubsub), listener.settings (module), 290 270, 276 ListenerAgent (class in listener.agent), 289 main() (in module economizer.copy_of_drivenagent), 272 load() (volttron.platform.keystore.BaseJSONStore main() (in module economizer.drivenagent), 272 method), 341 main() (in module eventperformance.agent), 276 load() (volttron.utils.persistance.PersistentDict method), main() (in module example.drivenagent), 288 347 main() (in module forwarder.agent), 235 load_config() (in module volttron.platform.agent.utils), main() (in module greeter.agent), 290 314 main() (in module ilc.agent), 279 load_create_store() (in module volttron.utils.persistance), main() (in module launcher.agent), 253, 291 347 main() (in module listener.agent), 289 load_state() (actuator.scheduler.ScheduleManager main() (in module master_driver.agent), 246 method), 231 main() (in module matlab_proxy.agent), 280 load_user() (in module volttron.platform.auth), 335 main() (in module mobileexample.agent), 290 LockError, 230 main() (in module mongodb.historian), 247 log() (volttron.platform.agent.driven.Results method), main() (in module mpc.agent), 281 310 main() (in module multibuilding.agent), 248 log_entries() (in module volttron.platform.aip), 332 main() (in module openadr.agent), 283 log_stream() (in module volttron.platform.aip), 332 main() (in module openeis.historian), 248 log_to_file() (in module volttron.platform.control), 339 main() (in module passiveafdd.passive_afdd), 283 log_to_file() (in module volttron.platform.main), 342 main() (in module pingpong.agent), 291 log_to_file() (in module volttron.platform.packaging), main() (in module schedule_example.agent), 292 345 main() (in module simpleforwarder.simpleforwarder), LogLevelAction (class in volttron.platform.main), 342 292 lookup_user_id() (volt- main() (in module smaphistorian.historian), 259 tron.platform.vip.router.BaseRouter method), main() (in module smds.agent), 283 329 main() (in module smdspush.agent), 284 loop() (volttron.platform.agent.base.BaseAgent method), main() (in module sqlhistorian.historian), 250 301 main() (in module subscriber.subscriber_agent), 289 loop() (volttron.platform.vip.agent.core.BasicCore main() (in module sysmon.agent), 251 method), 325 main() (in module thresholddetection.agent), 252

368 Index VOLTTRON Documentation, Release 3.5RC1 main() (in module vcplatform.agent), 258 match_test() (in module volt- main() (in module volttron.platform.control), 339 tron.platform.agent.matching), 311 main() (in module volttron.platform.main), 342 matlab_proxy (module), 281 main() (in module volttron.platform.packaging), 345 matlab_proxy.agent (module), 280 main() (in module volttroncentral.agent), 255 matlab_proxy_agent() (in module matlab_proxy.agent), make_current() (actuator.scheduler.Schedule method), 280 231 max_dx_time (economizer.economizer_rcx.EconCorrectlyOn make_current() (actuator.scheduler.Task method), 232 attribute), 274 make_gui() (mpc.MPC.MPC method), 281 max_dx_time (economizer.economizer_rcx.TempSensorDx manage() (vcplatform.agent.VolttronCentralPlatform attribute), 275 method), 258 mean() (in module volttron.platform.agent.math_utils), MapperCriterion (class in ilc.agent), 279 312 mark_dirty_point() (mas- meh() (in module volttron.platform.vip.agent.example), ter_driver.interfaces.RevertTracker method), 327 245 memory_percent() (sysmon.agent.SysMonAgent master_driver (module), 247 method), 251 master_driver.agent (module), 246 Message (class in volttron.platform.vip.socket), 330 master_driver.driver (module), 246 metadata (volttron.platform.packages.UnpackedPackage master_driver.driver_exceptions (module), 247 attribute), 344 master_driver.driver_locks (module), 247 method() (volttron.platform.jsonrpc.Dispatcher method), master_driver.interfaces (module), 240 340 master_driver.interfaces.bacnet (module), 235 MethodNotFound, 340 master_driver.interfaces.fakedriver (module), 236 mobileexample (module), 290 master_driver.interfaces.modbus (module), 236 mobileexample.agent (module), 290 master_driver.interfaces.radiothermostat (module), 237 MobileExampleAgent() (in module mobileexam- master_driver.interfaces.thermostat_api (module), 238 ple.agent), 290 master_driver.tests (module), 245 Modbus (class in volttron.drivers.modbus), 299 master_driver.tests.test_revert_mixin (module), 245 modbus_client() (in module mas- master_driver_agent() (in module master_driver.agent), ter_driver.interfaces.modbus), 237 246 ModbusBitRegister (class in mas- MasterDriverAgent (class in master_driver.agent), 246 ter_driver.interfaces.modbus), 236 MasterWebService (class in volttron.platform.web), 346 ModbusBitRegister (class in volttron.drivers.modbus), match() (volttron.platform.auth.AuthEntry method), 333 299 match() (volttron.platform.auth.List method), 335 ModbusByteRegister (class in mas- match() (volttron.platform.auth.String method), 335 ter_driver.interfaces.modbus), 236 match_all() (in module volt- ModbusByteRegister (class in volttron.drivers.modbus), tron.platform.agent.matching), 311 300 match_contains() (in module volt- ModbusInterface (class in volttron.drivers.modbus), 300 tron.platform.agent.matching), 311 ModbusInterfaceException, 237, 300 match_end() (in module volt- ModbusRegisterBase (class in mas- tron.platform.agent.matching), 311 ter_driver.interfaces.modbus), 237 match_exact() (in module volt- ModbusRegisterBase (class in volttron.drivers.modbus), tron.platform.agent.matching), 311 300 match_glob() (in module volt- mode() (master_driver.interfaces.thermostat_api.ThermostatInterface tron.platform.agent.matching), 311 method), 239 match_headers() (in module volt- model() (master_driver.interfaces.thermostat_api.ThermostatInterface tron.platform.agent.matching), 311 method), 239 match_regex() (in module volt- mongodb (module), 247 tron.platform.agent.matching), 311 mongodb.historian (module), 247 match_start() (in module volt- Monitor (class in volttron.platform.main), 342 tron.platform.agent.matching), 311 MPC (class in mpc.MPC), 281 match_subtopic() (in module volt- mpc (module), 283 tron.platform.agent.matching), 311 mpc.agent (module), 281 mpc.CBC_Gui (module), 281

Index 369 VOLTTRON Documentation, Release 3.5RC1 mpc.MPC (module), 281 on_match() (mpc.agent.MpcAgent method), 281 mpc.python_building (module), 282 on_request() (baseline.agent.BaselineAgent method), 269 mpc.python_control (module), 282 on_request() (cumulativesum.agent.CumulativeSumAgent MpcAgent (class in mpc.agent), 281 method), 269 multibuilding (module), 248 on_request() (eventperfor- multibuilding.agent (module), 247 mance.agent.EventPerformanceAgent method), MultiBuildingAgent() (in module multibuilding.agent), 275 247 onmessage() (emailer.agent.EmailerAgent method), 234 MySqlFuncts (class in sqlhistorian.db.mysqlfuncts), 249 onmessage() (in module weathertest), 259 onmessage() (standalonelistener.StandAloneListener N method), 293 name (volttron.platform.control.Agent attribute), 337 onmessage() (volttron.platform.vip.agent.example.ExampleAgent next() (volttron.platform.vip.agent.results.ResultsDictionary method), 327 method), 327 onsetup() (listener.agent.ListenerAgent method), 289 no_sat_stpt_reset() (air- onstart() (listener.agent.ListenerAgent method), 289 side.diagnostics.reset_sched_rcx.SchedResetRcx open() (volttron.platform.packages.BasePackageVerifier method), 263 method), 343 no_static_pr_reset() (air- open() (volttron.platform.packages.ZipPackageVerifier side.diagnostics.reset_sched_rcx.SchedResetRcx method), 344 method), 263 openadr (module), 283 nonblocking() (in module volttron.platform.vip.socket), openadr.agent (module), 283 330 OpenADRAgent() (in module openadr.agent), 283 normalize_matrix() (in module ilc.ilc_matrices), 280 openeis (module), 249 normtopic() (in module volt- openeis.historian (module), 248 tron.platform.messaging.utils), 318 openeis.settings (module), 249 not_economizing_when_needed() (econo- out_loop() (volttron.platform.vip.agent.compat.CompatPubSub mizer.economizer_rcx.EconCorrectlyOn method), 325 method), 274 output_format() (volttron.platform.agent.driven.AbstractDrivenAgent notify() (volttron.platform.agent.green.WaitQueue class method), 309 method), 310 over() (master_driver.interfaces.thermostat_api.ThermostatInterface notify() (volttron.platform.agent.multithreading.WaitQueue method), 239 method), 313 notify() (volttron.platform.control.Connection method), P 338 p_abstime() (in module volt- notify() (volttron.platform.jsonrpc.Dispatcher method), tron.platform.agent.base_historian), 308 341 p_error() (in module volt- notify() (volttron.platform.vip.agent.subsystems.RPC tron.platform.agent.base_historian), 308 method), 322 p_query_pair() (in module volt- notify() (volttron.platform.vip.agent.subsystems.rpc.RPC tron.platform.agent.base_historian), 308 method), 321 p_query_single() (in module volt- notify_all() (volttron.platform.agent.green.WaitQueue tron.platform.agent.base_historian), 308 method), 310 p_reltime() (in module volt- notify_all() (volttron.platform.agent.multithreading.WaitQueue tron.platform.agent.base_historian), 308 method), 313 p_timeref() (in module volt- now() (in module volttron.platform.agent.base_historian), tron.platform.agent.base_historian), 308 308 package() (volttroncentral.registry.PlatformRegistry method), 256 O package_name (volttron.platform.packages.UnpackedPackage oad_modulation_check() (afdd.afdd.AFDD method), 261 attribute), 344 on_heartbeat_topic() (vcplat- parse_config() (master_driver.interfaces.bacnet.Interface form.agent.VolttronCentralPlatform method), method), 235 258 parse_config() (master_driver.interfaces.fakedriver.Interface on_match() (greeter.agent.GreeterAgent method), 290 method), 236 on_match() (listener.agent.ListenerAgent method), 289

370 Index VOLTTRON Documentation, Release 3.5RC1 parse_config() (master_driver.interfaces.modbus.Interface ping_device() (bacnet_proxy.agent.BACnetProxyAgent method), 236 method), 233 parse_config() (master_driver.interfaces.radiothermostat.Interfaceping_target() (master_driver.interfaces.bacnet.Interface method), 237 method), 235 parse_config() (volttron.drivers.bacnet.BACnetInterface ping_target() (master_driver.interfaces.radiothermostat.Interface method), 295 method), 237 parse_config() (volttron.drivers.file_driver.FileInterface ping_target() (volttron.drivers.bacnet.BACnetInterface method), 299 method), 295 parse_config() (volttron.drivers.modbus.ModbusInterface pingpong (module), 291 method), 300 pingpong.agent (module), 291 parse_state() (volttron.drivers.base.InterfaceFloatActuator PingPongAgent() (in module pingpong.agent), 291 method), 297 platform_registry (volttroncen- parse_time() (in module volt- tral.resource_directory.ResourceDirectory tron.platform.agent.base_historian), 308 attribute), 257 parse_value() (master_driver.interfaces.modbus.ModbusBitRegisterplatform_uuid (volttroncentral.registry.RegistryEntry at- method), 236 tribute), 256 parse_value() (master_driver.interfaces.modbus.ModbusByteRegisterPlatformRegistry (class in volttroncentral.registry), 255 method), 237 poll (volttron.platform.vip.router.BaseRouter attribute), parse_value() (volttron.drivers.file_driver.FileRegister 329 method), 299 poll() (volttron.platform.agent.base.BaseAgent method), parse_value() (volttron.drivers.modbus.ModbusBitRegister 301 method), 299 poll_process() (launcher.agent.ProcessAgent method), parse_value() (volttron.drivers.modbus.ModbusByteRegister 253, 291 method), 300 pop_record_and_files() (volt- passiveafdd (module), 283 tron.platform.packages.VolttronPackageWheelFileNoSign passiveafdd() (in module passiveafdd.passive_afdd), 283 method), 343 passiveafdd.passive_afdd (module), 283 pop_records_file() (volt- PEER (volttron.platform.vip.agent.compat.CompatPubSub tron.platform.packages.VolttronPackageWheelFileNoSign attribute), 324 method), 343 PeerList (class in volt- populate_schedule() (actuator.scheduler.Task method), tron.platform.vip.agent.subsystems), 322 232 PeerList (class in volt- post_action() (volttron.platform.config.DebugArgumentParser tron.platform.vip.agent.subsystems.peerlist), method), 337 320 post_action() (volttron.platform.config.TrackingArgumentParser period (volttron.platform.agent.sched.RecurringEvent at- method), 337 tribute), 313 prctl() (in module volttron.platform.lib.prctl), 315 periodic() (in module volttron.platform.agent.base), 301 pre_action() (volttron.platform.config.TrackingArgumentParser periodic() (volttron.platform.vip.agent.core.BasicCore method), 337 method), 325 pre_message() (economizer.economizer_rcx.Application periodic_read() (master_driver.driver.DriverAgent method), 273 method), 246 preempt() (actuator.scheduler.Task method), 232 periodic_timer() (volttron.platform.agent.base.BaseAgent preprocess_option() (volt- method), 301 tron.platform.config.ArgumentParser method), PersistentDict (class in volttron.utils.persistance), 347 336 Ping (class in volttron.platform.vip.agent.subsystems), print_tb() (volttron.platform.jsonrpc.RemoteError 322 method), 340 Ping (class in volttron.platform.vip.agent.subsystems.ping), prioritize_agent() (volttron.platform.aip.AIPplatform 320 method), 331 ping() (volttron.platform.vip.agent.subsystems.Ping prioritize_agent() (volt- method), 322 tron.platform.control.ControlService method), ping() (volttron.platform.vip.agent.subsystems.ping.Ping 338 method), 320 priority() (in module volttron.platform.control), 339 ping_back() (volttron.platform.agent.base.PublishMixin process_request() (baseline.agent.BaselineAgent method), 303 method), 269

Index 371 VOLTTRON Documentation, Release 3.5RC1 process_request() (cumula- 258 tivesum.agent.CumulativeSumAgent method), publish_to_smap() (afdd.afdd.AFDD method), 261 269 PublishMixin (class in volttron.platform.agent.base), 303 process_request() (eventperfor- PubSub (class in volttron.platform.vip.agent.subsystems), mance.agent.EventPerformanceAgent method), 322 276 PubSub (class in volt- process_row() (volttron.platform.agent.driven.ConversionMapper tron.platform.vip.agent.subsystems.pubsub), method), 310 321 process_task() (bacnet_proxy.agent.BACnet_application PubSubService (class in volttron.platform.main), 342 method), 234 PushAgent() (in module smdspush.agent), 284 process_task() (volttron.drivers.bacnet.BACnet_application method), 296 Q process_wait() (in module volttron.platform.aip), 332 qs (volttron.platform.vip.socket.Address attribute), 330 ProcessAgent (class in launcher.agent), 253, 291 Query (class in volt- prompt_response() (in module volttron.platform.config), tron.platform.vip.agent.subsystems.query), 337 321 ProtocolError, 330 query() (sqlhistorian.db.basedb.DbDriver method), 249 prune_to_current() (actuator.scheduler.Schedule method), query() (sqlhistorian.db.mysqlfuncts.MySqlFuncts 231 method), 250 pstdev() (in module volttron.platform.agent.math_utils), query() (sqlhistorian.db.sqlitefuncts.SqlLiteFuncts 312 method), 250 public() (volttron.platform.keystore.KeyStore method), query() (volttron.platform.agent.base_historian.BaseQueryHistorianAgent 341 method), 307 publish() (in module tests.test_actuator_pubsub), 232 query() (volttron.platform.vip.agent.subsystems.query.Query publish() (volttron.platform.agent.base.PublishMixin method), 321 method), 303 query_historian() (volt- publish() (volttron.platform.vip.agent.subsystems.Heartbeat tron.platform.agent.base_historian.BaseQueryHistorianAgent method), 323 method), 307 publish() (volttron.platform.vip.agent.subsystems.heartbeat.Heartbeatquery_topic_list() (volt- method), 319 tron.platform.agent.base_historian.BaseQueryHistorianAgent publish() (volttron.platform.vip.agent.subsystems.PubSub method), 308 method), 323 Queue (class in volttron.platform.agent.sched), 313 publish() (volttron.platform.vip.agent.subsystems.pubsub.PubSub method), 321 R publish_address (volttron.platform.aip.AIPplatform at- read() (volttron.drivers.base.BaseSmapVolttron method), tribute), 331 297 PUBLISH_ADDRESS (volt- read() (volttron.drivers.data_logger.DataLogger method), tron.platform.vip.agent.compat.CompatPubSub 298 attribute), 324 read() (volttron.platform.auth.AuthFile method), 333 publish_ex() (volttron.platform.agent.base.PublishMixin read() (volttron.platform.lib.inotify.green.inotify method), 303 method), 314 publish_heartbeat() (greeter.agent.GreeterAgent method), read_allow_entries() (volttron.platform.auth.AuthFile 290 method), 333 publish_heartbeat() (standalonelis- read_auth_file() (volttron.platform.auth.AuthService tener.StandAloneListener method), 293 method), 335 publish_json() (volttron.platform.agent.base.PublishMixin read_callback() (volttron.drivers.base.BaseSmapVolttron method), 303 method), 297 publish_lock() (in module master_driver.driver_locks), read_errback() (volttron.drivers.base.BaseSmapVolttron 247 method), 297 publish_to_historian() (volt- read_properties() (bacnet_proxy.agent.BACnetProxyAgent tron.platform.agent.base_historian.BaseHistorianAgent method), 233 method), 306 receiver() (volttron.platform.vip.agent.core.BasicCore publish_to_peers() (vcplat- class method), 325 form.agent.VolttronCentralPlatform method),

372 Index VOLTTRON Documentation, Release 3.5RC1 receiver() (volttron.platform.vip.agent.dispatch.Signal remove_by_index() (volttron.platform.auth.AuthFile method), 326 method), 333 reconfigure() (sysmon.agent.SysMonAgent method), 251 remove_by_indices() (volttron.platform.auth.AuthFile reconfigure() (vcplatform.agent.VolttronCentralPlatform method), 334 method), 258 remove_files() (volttron.platform.packages.VolttronPackageWheelFileNoSign RecurringEvent (class in volttron.platform.agent.sched), method), 343 313 remove_successfully_published() (volt- recv_message() (volttron.platform.messaging.socket.Socket tron.platform.agent.base_historian.BackupDatabase method), 317 method), 305 recv_message_ex() (volt- repack() (volttron.platform.packages.UnpackedPackage tron.platform.messaging.socket.Socket method), 344 method), 317 repackage() (in module volttron.platform.packaging), 345 Register (class in master_driver.interfaces.bacnet), 236 report_all_handled() (volt- Register (class in mas- tron.platform.agent.base_historian.BaseHistorianAgent ter_driver.interfaces.radiothermostat), 238 method), 306 register() (volttron.platform.vip.agent.core.Core method), report_handled() (volttron.platform.agent.base_historian.BaseHistorianAgent 325 method), 306 register() (volttroncentral.registry.PlatformRegistry request() (volttron.drivers.bacnet.BACnet_application method), 256 method), 296 register_agent_route() (volt- request_cancel_schedule() (actuator.agent.ActuatorAgent tron.platform.web.MasterWebService method), method), 229 346 request_discovery_info() (volt- register_criterion() (in module ilc.agent), 279 tron.platform.web.DiscoveryInfo static register_instance() (in module rpc_test_client), 253 method), 346 register_instance() (volttroncen- request_new_schedule() (actuator.agent.ActuatorAgent tral.agent.VolttronCentralAgent method), method), 229 255 request_slots() (actuator.scheduler.ScheduleManager register_path_route() (volt- method), 231 tron.platform.web.MasterWebService method), RequestResult (class in actuator.scheduler), 231 347 reserve_soft_resources() (volt- register_platform() (in module rpc_test_client), 253 tron.platform.resmon.ResourceMonitor register_service() (vcplat- method), 345 form.agent.VolttronCentralPlatform method), reset() (volttron.platform.vip.socket.Address method), 258 330 RegistryEntry (class in volttroncentral.registry), 256 reset() (volttron.platform.vip.tracking.Tracker method), reinitialize() (airside.diagnostics.reset_sched_rcx.SchedResetRcx 330 method), 263 reset_currently_curtailed() (ilc.agent.Clusters method), reinitialize() (airside.diagnostics.satemp_rcx.SupplyTempRcx 278 method), 265 reset_currently_curtailed() (ilc.agent.Criteria method), reinitialize() (airside.diagnostics.stcpr_rcx.DuctStaticRcx 278 method), 266 reset_currently_curtailed() (ilc.agent.Device method), remote_url() (in module settings), 292, 293 279 RemoteError, 340 reset_curtail_count() (ilc.agent.Clusters method), 278 remove_agent() (in module volttron.platform.control), reset_curtail_count() (ilc.agent.Criteria method), 278 339 reset_curtail_count() (ilc.agent.Device method), 279 remove_agent() (volttron.platform.aip.AIPplatform ResourceDirectory (class in volttroncen- method), 331 tral.resource_directory), 257 remove_agent() (volttron.platform.control.ControlService ResourceError, 345 method), 338 ResourceMonitor (class in volttron.platform.resmon), 345 remove_auth() (in module volttron.platform.control), 339 restart() (volttron.platform.vip.agent.subsystems.Heartbeat remove_bus() (volttron.platform.vip.agent.subsystems.PubSub method), 323 method), 323 restart() (volttron.platform.vip.agent.subsystems.heartbeat.Heartbeat remove_bus() (volttron.platform.vip.agent.subsystems.pubsub.PubSubmethod), 319 method), 321 restart_agent() (in module volttron.platform.control), 339

Index 373 VOLTTRON Documentation, Release 3.5RC1 restart_agent() (vcplatform.agent.VolttronCentralPlatform run() (drivenmatlab.matlab.Application method), 271, method), 258 277 restart_agent() (volttron.platform.control.ControlService run() (economizer.economizer_rcx.Application method), method), 338 273 result() (volttron.platform.jsonrpc.Dispatcher method), run() (example.drivencontrol.Application method), 289 341 run() (mpc.CBC_Gui.CBC_Gui method), 281 Results (class in volttron.platform.agent.driven), 310 run() (volttron.platform.agent.base.BaseAgent method), ResultsDictionary (class in volt- 302 tron.platform.vip.agent.results), 327 run() (volttron.platform.agent.driven.AbstractDrivenAgent revert_all() (master_driver.driver.DriverAgent method), method), 310 246 run() (volttron.platform.main.Monitor method), 342 revert_all() (master_driver.interfaces.bacnet.Interface run() (volttron.platform.vip.agent.core.BasicCore method), 235 method), 325 revert_all() (master_driver.interfaces.BaseInterface run() (volttron.platform.vip.router.BaseRouter method), method), 242 329 revert_all() (master_driver.interfaces.BasicRevert run_agent() (in module volttron.platform.agent.utils), 314 method), 244 run_agent() (in module volttron.platform.control), 339 revert_all() (master_driver.interfaces.radiothermostat.Interfacerun_all() (afdd.afdd.AFDD method), 261 method), 238 run_control() (mpc.agent.MpcAgent method), 281 revert_device() (actuator.agent.ActuatorAgent method), run_control() (mpc.MPC.MPC method), 281 229 run_control() (mpc.python_control.Control method), 282 revert_device() (master_driver.agent.MasterDriverAgent run_dir (volttron.platform.aip.AIPplatform attribute), 331 method), 246 revert_point() (actuator.agent.ActuatorAgent method), S 229 sat_rcx() (airside.diagnostics.satemp_rcx.SupplyTempRcx revert_point() (master_driver.agent.MasterDriverAgent method), 265 method), 246 save_state() (actuator.scheduler.ScheduleManager revert_point() (master_driver.driver.DriverAgent method), 231 method), 246 saybye() (volttron.platform.vip.agent.example.ExampleAgent revert_point() (master_driver.interfaces.bacnet.Interface method), 327 method), 235 sayhi() (volttron.platform.vip.agent.example.ExampleAgent revert_point() (master_driver.interfaces.BaseInterface method), 327 method), 242 scale_time() (in module mpc.MPC), 281 revert_point() (master_driver.interfaces.BasicRevert sched_rcx_alg() (airside.diagnostics.reset_sched_rcx.SchedResetRcx method), 244 method), 263 revert_point() (master_driver.interfaces.radiothermostat.InterfaceSchedResetRcx (class in air- method), 238 side.diagnostics.reset_sched_rcx), 263 RevertTracker (class in master_driver.interfaces), 244 Schedule (class in actuator.scheduler), 231 rollback() (sqlhistorian.db.basedb.DbDriver method), 249 schedule() (in module volttron.platform.agent.cron), 309 route() (volttron.platform.vip.router.BaseRouter method), schedule() (volttron.platform.agent.base.BaseAgent 329 method), 302 route_request() (vcplat- schedule() (volttron.platform.agent.sched.Queue form.agent.VolttronCentralPlatform method), method), 313 258 schedule() (volttron.platform.vip.agent.core.BasicCore Router (class in volttron.platform.main), 342 method), 325 RPC (class in volttron.platform.vip.agent.subsystems), schedule_example (module), 292 322 schedule_example() (in module sched- RPC (class in volttron.platform.vip.agent.subsystems.rpc), ule_example.agent), 292 321 schedule_example.agent (module), 292 rpc_test_client (module), 253 schedule_example.settings (module), 292 run() (afdd.afdd.AFDD method), 261 schedule_ping() (master_driver.interfaces.bacnet.Interface run() (airside.airside_retuning_rcx.Application method), method), 235 268 schedule_slot() (actuator.scheduler.Schedule method), 231

374 Index VOLTTRON Documentation, Release 3.5RC1

ScheduleError, 231 send_alert1() (alerter.agent.AlerterAgent method), 285 ScheduleManager (class in actuator.scheduler), 231 send_async() (volttron.platform.vip.agent.core.BasicCore scrape_all() (master_driver.interfaces.bacnet.Interface method), 325 method), 235 send_message() (volttron.platform.messaging.socket.Socket scrape_all() (master_driver.interfaces.BaseInterface method), 317 method), 242 send_message_ex() (volt- scrape_all() (master_driver.interfaces.BasicRevert tron.platform.messaging.socket.Socket method), 244 method), 317 scrape_all() (master_driver.interfaces.radiothermostat.Interfacesend_string() (volttron.platform.messaging.socket.Socket method), 238 method), 317 scrape_all() (volttron.drivers.bacnet.BACnetInterface sendby() (volttron.platform.vip.agent.dispatch.Signal method), 295 method), 326 scrape_all() (volttron.drivers.base.BaseInterface method), sensor_error_check() (afdd.afdd.AFDD method), 261 296 serialize() (volttron.platform.jsonrpc.Dispatcher method), scrape_all() (volttron.drivers.file_driver.FileInterface 341 method), 299 server (volttron.platform.control.Connection attribute), scrape_all() (volttron.drivers.modbus.ModbusInterface 338 method), 300 serverkey (volttroncentral.registry.RegistryEntry at- scrape_all_callback() (volt- tribute), 256 tron.drivers.bacnet.BACnetInterface method), serverkey() (volttron.platform.keystore.KnownHostsStore 295 method), 341 scrape_bit_registers() (mas- SessionHandler (class in volttroncentral.sessions), 257 ter_driver.interfaces.modbus.Interface method), set() (bacnet_proxy.agent.IOCB method), 234 236 set_cool_pgm() (master_driver.interfaces.thermostat_api.ThermostatInterface scrape_bit_registers() (volt- method), 239 tron.drivers.modbus.ModbusInterface method), set_deadbands() (mpc.python_building.Building 300 method), 282 scrape_byte_registers() (mas- set_default() (master_driver.interfaces.BasicRevert ter_driver.interfaces.modbus.Interface method), method), 244 236 set_default() (master_driver.interfaces.RevertTracker scrape_byte_registers() (volt- method), 245 tron.drivers.modbus.ModbusInterface method), set_exception() (bacnet_proxy.agent.IOCB method), 234 300 set_fan_mode() (mpc.python_building.Building method), scrape_ending() (master_driver.agent.MasterDriverAgent 282 method), 246 set_groups() (volttron.platform.auth.AuthFile method), scrape_starting() (master_driver.agent.MasterDriverAgent 334 method), 246 set_heat_pgm() (master_driver.interfaces.thermostat_api.ThermostatInterface secret() (volttron.platform.keystore.KeyStore method), method), 239 341 set_hvac_mode() (mpc.python_building.Building select() (sqlhistorian.db.basedb.DbDriver method), 249 method), 282 select_fan() (mpc.CBC_Gui.ZoneGui method), 281 set_lower_limit() (mpc.python_control.Control method), select_heat_cool() (mpc.CBC_Gui.ZoneGui method), 282 281 set_max_units() (mpc.python_control.Control method), send() (volttron.platform.async.AsyncCall method), 332 282 send() (volttron.platform.async.Threadlet method), 332 set_outdoor_temp() (mpc.MPC.MPC method), 281 send() (volttron.platform.vip.agent.core.BasicCore set_outdoor_temp() (mpc.python_building.Building method), 325 method), 282 send() (volttron.platform.vip.agent.dispatch.Signal set_outside_temp() (mpc.python_control.Control method), 326 method), 282 send_agent() (in module volttron.platform.control), 339 set_period() (volttron.platform.vip.agent.subsystems.Heartbeat send_alert() (volttron.platform.vip.agent.subsystems.Health method), 323 method), 324 set_period() (volttron.platform.vip.agent.subsystems.heartbeat.Heartbeat send_alert() (volttron.platform.vip.agent.subsystems.health.Health method), 319 method), 319 set_point() (actuator.agent.ActuatorAgent method), 230

Index 375 VOLTTRON Documentation, Release 3.5RC1 set_point() (master_driver.agent.MasterDriverAgent set_state_async_callback() (volt- method), 246 tron.drivers.bacnet.BACnetRegister method), set_point() (master_driver.driver.DriverAgent method), 295 246 set_state_callback() (volt- set_point() (master_driver.interfaces.bacnet.Interface tron.drivers.modbus.ModbusBitRegister method), 235 method), 300 set_point() (master_driver.interfaces.BaseInterface set_state_callback() (volt- method), 242 tron.drivers.modbus.ModbusByteRegister set_point() (master_driver.interfaces.BasicRevert method), 300 method), 244 set_state_sync() (volttron.drivers.bacnet.BACnetRegister set_point() (master_driver.interfaces.radiothermostat.Interface method), 295 method), 238 set_state_sync() (volttron.drivers.modbus.ModbusBitRegister set_point_async() (volt- method), 300 tron.drivers.bacnet.BACnetInterface method), set_state_sync() (volttron.drivers.modbus.ModbusByteRegister 295 method), 300 set_point_async() (volttron.drivers.base.BaseInterface set_status() (volttron.platform.vip.agent.subsystems.Health method), 296 method), 324 set_point_async() (volt- set_status() (volttron.platform.vip.agent.subsystems.health.Health tron.drivers.file_driver.FileInterface method), method), 319 299 set_upper_limit() (mpc.python_control.Control method), set_point_async() (volt- 282 tron.drivers.modbus.ModbusInterface method), set_value() (volttron.drivers.file_driver.FileRegister 300 method), 299 set_point_sync() (volttron.drivers.bacnet.BACnetInterface set_zone_temp() (mpc.python_control.Control method), method), 295 282 set_point_sync() (volttron.drivers.base.BaseInterface setdefault() (volttron.platform.messaging.headers.Headers method), 296 method), 315 set_point_sync() (volttron.drivers.file_driver.FileInterface setdefault() (volttron.platform.messaging.socket.Headers method), 299 method), 317 set_point_sync() (volttron.drivers.modbus.ModbusInterface setpoint_control_check() (in module air- method), 300 side.diagnostics.common), 262 set_points_name() (afdd.afdd.AFDD method), 261 settings (module), 292, 293 set_roles() (volttron.platform.auth.AuthFile method), 334 setup() (askbot.agent.AskAgent method), 286 set_setting() (vcplatform.agent.VolttronCentralPlatform setup() (baseline.agent.BaselineAgent method), 269 method), 258 setup() (cumulativesum.agent.CumulativeSumAgent set_state() (master_driver.interfaces.modbus.ModbusBitRegister method), 269 method), 236 setup() (eventperformance.agent.EventPerformanceAgent set_state() (master_driver.interfaces.modbus.ModbusByteRegister method), 276 method), 237 setup() (greeter.agent.GreeterAgent method), 290 set_state() (volttron.drivers.base.InterfaceBitActuator setup() (launcher.agent.ProcessAgent method), 253, 291 method), 297 setup() (mpc.agent.MpcAgent method), 281 set_state() (volttron.drivers.base.InterfaceFloatActuator setup() (volttron.drivers.bacnet.BACnet method), 295 method), 297 setup() (volttron.drivers.base.BaseSmapVolttron method), set_state() (volttron.drivers.base.InterfaceIntActuator 297 method), 297 setup() (volttron.drivers.base.InterfaceBitActuator set_state_async() (volt- method), 297 tron.drivers.bacnet.BACnetRegister method), setup() (volttron.drivers.base.InterfaceFloatActuator 295 method), 297 set_state_async() (volt- setup() (volttron.drivers.base.InterfaceIntActuator tron.drivers.modbus.ModbusBitRegister method), 297 method), 300 setup() (volttron.drivers.data_logger.DataLogger set_state_async() (volt- method), 298 tron.drivers.modbus.ModbusByteRegister setup() (volttron.drivers.file_driver.File method), 298 method), 300 setup() (volttron.drivers.modbus.Modbus method), 299

376 Index VOLTTRON Documentation, Release 3.5RC1 setup() (volttron.platform.agent.base.BaseAgent smdspush (module), 285 method), 302 smdspush.agent (module), 284 setup() (volttron.platform.aip.AIPplatform method), 331 Socket (class in volttron.platform.messaging.socket), 317 setup() (volttron.platform.main.Router method), 342 Socket (class in volttron.platform.vip), 330 setup() (volttron.platform.vip.agent.compat.CompatPubSub Socket (class in volttron.platform.vip.green), 328 method), 325 socket_lock() (in module master_driver.driver_locks), setup() (volttron.platform.vip.agent.core.BasicCore 247 method), 325 spawn() (in module volt- setup() (volttron.platform.vip.agent.example.ExampleAgent tron.platform.vip.agent.decorators), 326 method), 327 spawn() (volttron.platform.vip.agent.core.BasicCore setup() (volttron.platform.vip.router.BaseRouter method), method), 325 329 spawn_in_thread() (volt- setup_agent() (volttron.platform.main.PubSubService tron.platform.vip.agent.core.BasicCore method), 342 method), 325 setup_conversion_map() (volt- spawn_later() (volttron.platform.vip.agent.core.BasicCore tron.platform.agent.driven.ConversionMapper method), 325 method), 310 split() (volttron.platform.config.ListAction method), 337 setup_device() (bacnet_proxy.agent.BACnetProxyAgent sqlhistorian (module), 251 method), 234 sqlhistorian.db (module), 250 setup_device() (master_driver.driver.DriverAgent sqlhistorian.db.basedb (module), 249 method), 246 sqlhistorian.db.mysqlfuncts (module), 249 setup_device() (volttron.drivers.bacnet.BACnetInterface sqlhistorian.db.sqlitefuncts (module), 250 method), 295 sqlhistorian.historian (module), 250 setup_publisher() (volt- sqlhistorian.settings (module), 251 tron.drivers.data_logger.DataLogger method), SqlLiteFuncts (class in sqlhistorian.db.sqlitefuncts), 250 298 StandAloneListener (class in standalonelistener), 293 setup_subscriber() (volt- standalonelistener (module), 293 tron.drivers.data_logger.DataLogger method), StandAloneWithAuth (class in standalonewithauth), 294 298 start (actuator.scheduler.TimeSlice attribute), 232 setup_zap() (volttron.platform.auth.AuthService start() (standalonelistener.StandAloneListener method), method), 335 293 shutdown() (volttron.platform.agent.driven.AbstractDrivenAgentstart() (sysmon.agent.SysMonAgent method), 251 method), 310 start() (thresholddetection.agent.ThresholdDetectionAgent shutdown() (volttron.platform.aip.AIPplatform method), method), 252 331 start() (volttron.drivers.base.BaseSmapVolttron method), shutdown() (volttron.platform.control.ControlService 297 method), 338 start() (volttron.drivers.data_logger.DataLogger method), shutdown_agents() (in module volttron.platform.control), 298 339 start() (volttron.platform.vip.agent.subsystems.Heartbeat Signal (class in volttron.platform.vip.agent.dispatch), 326 method), 323 simpleforwarder (module), 292 start() (volttron.platform.vip.agent.subsystems.heartbeat.Heartbeat simpleforwarder() (in module simplefor- method), 320 warder.simpleforwarder), 292 start() (volttron.platform.vip.router.BaseRouter method), simpleforwarder.simpleforwarder (module), 292 329 sleep() (in module volttron.platform.agent.green), 310 start_agent() (in module volttron.platform.control), 339 smaphistorian (module), 260 start_agent() (vcplatform.agent.VolttronCentralPlatform smaphistorian.historian (module), 259 method), 258 smaphistorian.settings (module), 260 start_agent() (volttron.platform.aip.AIPplatform method), SMAPHistorianAgent() (in module smaphisto- 331 rian.historian), 259 start_agent() (volttron.platform.control.ControlService smds (module), 284 method), 338 smds.agent (module), 283 start_agent_thread() (in module volt- smds.settings (module), 284 tron.platform.agent.utils), 314 SMDSAgent() (in module smds.agent), 283

Index 377 VOLTTRON Documentation, Release 3.5RC1 start_volttron_process() (in module volt- stop_agent() (vcplatform.agent.VolttronCentralPlatform tron.platform.main), 342 method), 258 start_with_period() (volt- stop_agent() (volttron.platform.aip.AIPplatform method), tron.platform.vip.agent.subsystems.Heartbeat 331 method), 323 stop_agent() (volttron.platform.control.ControlService start_with_period() (volt- method), 338 tron.platform.vip.agent.subsystems.heartbeat.Heartbeatstop_platform() (volttron.platform.control.ControlService method), 320 method), 338 starting() (alerter.agent.AlerterAgent method), 285 stop_zap() (volttron.platform.auth.AuthService method), starting() (master_driver.agent.MasterDriverAgent 335 method), 246 stoping() (vcplatform.agent.VolttronCentralPlatform starting() (master_driver.driver.DriverAgent method), 246 method), 258 starting() (volttron.platform.vip.agent.example.ExampleAgentstopping() (volttron.platform.agent.base_historian.BaseHistorianAgent method), 327 method), 306 starting_base() (volttron.platform.agent.base_historian.BaseHistorianAgentstopping() (volttron.platform.vip.agent.example.ExampleAgent method), 306 method), 327 startupagent() (volttron.platform.web.MasterWebService store() (volttron.platform.keystore.BaseJSONStore method), 347 method), 341 STATE_FINISHED (actuator.scheduler.Task attribute), stretch_to_include() (actuator.scheduler.TimeSlice 232 method), 232 STATE_PRE_RUN (actuator.scheduler.Task attribute), String (class in volttron.platform.auth), 335 232 strptime_tz() (in module volt- STATE_PREEMTED (actuator.scheduler.Task attribute), tron.platform.agent.base_historian), 308 232 submit_request() (bacnet_proxy.agent.BACnet_application STATE_RUNNING (actuator.scheduler.Task attribute), method), 234 232 submit_request() (volttron.drivers.bacnet.BACnet_application Status (class in volttron.platform.messaging.health), 315 method), 296 status (volttron.platform.messaging.health.Status at- SubParsersAction (class in volttron.platform.config), 337 tribute), 316 subscribe() (volttron.drivers.data_logger.DataLogger status_agents() (in module volttron.platform.control), 339 method), 298 status_agents() (vcplat- subscribe() (volttron.platform.agent.base.BaseAgent form.agent.VolttronCentralPlatform method), method), 302 258 subscribe() (volttron.platform.vip.agent.subsystems.PubSub status_agents() (volttron.platform.aip.AIPplatform method), 323 method), 331 subscribe() (volttron.platform.vip.agent.subsystems.pubsub.PubSub status_agents() (volttron.platform.control.ControlService method), 321 method), 338 subscribe_address (volttron.platform.aip.AIPplatform at- status_bad() (alerter.agent.AlerterAgent method), 285 tribute), 331 status_good() (alerter.agent.AlerterAgent method), 285 SUBSCRIBE_ADDRESS (volt- StatusCriterion (class in ilc.agent), 279 tron.platform.vip.agent.compat.CompatPubSub stdev() (in module volttron.platform.agent.math_utils), attribute), 324 312 subscriber (module), 289 step() (volttron.platform.agent.base.BaseAgent method), subscriber.settings (module), 289 302 subscriber.subscriber_agent (module), 289 stop() (volttron.platform.vip.agent.core.BasicCore subscriber_agent() (in module sub- method), 325 scriber.subscriber_agent), 289 stop() (volttron.platform.vip.agent.subsystems.Heartbeat SubsystemBase (class in volt- method), 323 tron.platform.vip.agent.subsystems.base), stop() (volttron.platform.vip.agent.subsystems.heartbeat.Heartbeat 318 method), 320 success (actuator.scheduler.RequestResult attribute), 231 stop() (volttron.platform.vip.router.BaseRouter method), SupplyTempRcx (class in air- 329 side.diagnostics.satemp_rcx), 264 stop_agent() (in module volttron.platform.control), 339 sync() (volttron.utils.persistance.PersistentDict method), 347

378 Index VOLTTRON Documentation, Release 3.5RC1 synchronize() (volttron.platform.vip.agent.subsystems.PubSubtest_contains() (in module volt- method), 323 tron.platform.agent.matching), 312 synchronize() (volttron.platform.vip.agent.subsystems.pubsub.PubSubtest_end() (in module volttron.platform.agent.matching), method), 321 312 sysmon (module), 252 test_exact() (in module volt- sysmon.agent (module), 251 tron.platform.agent.matching), 312 sysmon_agent() (in module sysmon.agent), 251 test_glob() (in module volttron.platform.agent.matching), SysMonAgent (class in sysmon.agent), 251 312 test_malformed_bad_device() (in module T tests.test_scheduler), 233 t_cool() (master_driver.interfaces.thermostat_api.ThermostatInterfacetest_malformed_schdeule_bad_timestr() (in module method), 239 tests.test_scheduler), 233 t_error() (in module volt- test_malformed_schedule() (in module tron.platform.agent.base_historian), 308 tests.test_scheduler), 233 t_heat() (master_driver.interfaces.thermostat_api.ThermostatInterfacetest_non_conflict_schedule() (in module method), 239 tests.test_scheduler), 233 t_LVALUE() (in module volt- test_regex() (in module volt- tron.platform.agent.base_historian), 308 tron.platform.agent.matching), 312 t_newline() (in module volt- test_schedule_conflict() (in module tests.test_scheduler), tron.platform.agent.base_historian), 309 233 t_NUMBER() (in module volt- test_schedule_self_conflict() (in module tron.platform.agent.base_historian), 308 tests.test_scheduler), 233 t_QSTRING() (in module volt- test_subtopic() (in module volt- tron.platform.agent.base_historian), 308 tron.platform.agent.matching), 312 t_setpoint() (master_driver.interfaces.thermostat_api.ThermostatInterfacetest_touching_requests() (in module tests.test_scheduler), method), 239 233 tag (volttron.platform.control.Agent attribute), 337 test_two_agents_two_devices() (in module tag_agent() (in module volttron.platform.control), 339 tests.test_scheduler), 233 tag_agent() (volttron.platform.aip.AIPplatform method), test_two_devices() (in module tests.test_scheduler), 233 331 tests (module), 233 tag_agent() (volttron.platform.control.ControlService tests.test_actuator_pubsub (module), 232 method), 338 tests.test_actuator_rpc (module), 233 tags (volttroncentral.registry.RegistryEntry attribute), 256 tests.test_scheduler (module), 233 Task (class in actuator.scheduler), 231 Thermostat_API() (in module mas- task_id (actuator.scheduler.DeviceState attribute), 231 ter_driver.interfaces.thermostat_api), 239 temperature_sensor_dx() (econo- ThermostatInterface (class in mas- mizer.economizer_rcx.TempSensorDx ter_driver.interfaces.thermostat_api), 238 method), 275 Threadlet (class in volttron.platform.async), 332 TempSensorDx (class in economizer.economizer_rcx), thresholddetection (module), 253 275 thresholddetection.agent (module), 252 terminate() (volttron.platform.agent.driven.Results thresholddetection_agent() (in module thresholddetec- method), 310 tion.agent), 252 test_basic() (in module tests.test_scheduler), 233 ThresholdDetectionAgent (class in thresholddetec- test_conflict_override() (in module tests.test_scheduler), tion.agent), 252 233 time_remaining (actuator.scheduler.DeviceState at- test_conflict_override_error() (in module tribute), 231 tests.test_scheduler), 233 Timeout, 310, 312 test_conflict_override_fail_on_running_agent() (in mod- timer() (volttron.platform.agent.base.BaseAgent ule tests.test_scheduler), 233 method), 302 test_conflict_override_success_running_agent() (in mod- TimeSlice (class in actuator.scheduler), 232 ule tests.test_scheduler), 233 timestamp (economizer.economizer_rcx.InsufficientOA test_conflict_override_success_running_agent2() (in attribute), 275 module tests.test_scheduler), 233 timestamp() (emailer.agent.EmailerAgent method), 234

Index 379 VOLTTRON Documentation, Release 3.5RC1

TimestampFromDatetime() (in module smdspush.agent), update_clean_values() (mas- 284 ter_driver.interfaces.RevertTracker method), Topic (class in volttron.platform.messaging.utils), 318 245 Tracker (class in volttron.platform.vip.tracking), 330 update_devices() (volttroncen- TrackingArgumentParser (class in volt- tral.registry.PlatformRegistry method), 256 tron.platform.config), 337 update_performance() (volttroncen- TrackingString (class in volttron.platform.config), 337 tral.registry.PlatformRegistry method), 256 tstat() (master_driver.interfaces.thermostat_api.ThermostatInterfaceupdate_sibling_address_cache() (vcplat- method), 239 form.agent.VolttronCentralPlatform method), 258 U update_status() (volttron.platform.messaging.health.Status unbind_zap() (volttron.platform.auth.AuthService method), 316 method), 335 update_topic() (sqlhistorian.db.basedb.DbDriver UnknownSubsystem, 327 method), 249 unocc_fan_operation() (air- update_topic_query() (sqlhisto- side.diagnostics.reset_sched_rcx.SchedResetRcx rian.db.mysqlfuncts.MySqlFuncts method), method), 263 250 unpack() (volttron.platform.packages.VolttronPackageWheelFileNoSignupdate_topic_query() (sqlhisto- method), 343 rian.db.sqlitefuncts.SqlLiteFuncts method), unpack_legacy_message() (in module volt- 250 tron.platform.vip.agent.compat), 325 uuid (volttron.platform.control.Agent attribute), 337 unpackage() (volttroncentral.registry.PlatformRegistry method), 256 V UnpackedPackage (class in volttron.platform.packages), valid_credentials() (volttron.platform.auth.AuthEntry 344 static method), 333 Unreachable, 327 valid_state() (volttron.drivers.base.InterfaceFloatActuator unregister() (volttroncentral.registry.PlatformRegistry method), 297 method), 256 validate_input() (in module ilc.ilc_matrices), 280 unregister_all_agent_routes() (volt- validation_builder() (in module air- tron.platform.web.MasterWebService method), side.diagnostics.common), 262 347 value (master_driver.interfaces.fakedriver.EKGregister unregister_platform() (volttroncen- attribute), 236 tral.agent.VolttronCentralAgent method), vcp_publickey (volttroncentral.registry.RegistryEntry at- 255 tribute), 257 unsubscribe() (volttron.platform.agent.base.BaseAgent vcplatform (module), 259 method), 302 vcplatform.agent (module), 257 unsubscribe() (volttron.platform.vip.agent.subsystems.PubSubverify_add_task() (in module tests.test_scheduler), 233 method), 323 vformat() (volttron.platform.messaging.utils.Topic unsubscribe() (volttron.platform.vip.agent.subsystems.pubsub.PubSubmethod), 318 method), 321 vip_address (volttroncentral.registry.RegistryEntry unsubscribe_all() (volt- attribute), 257 tron.platform.agent.base.BaseAgent method), VIPError, 326 302 volttron (module), 348 update() (volttron.platform.keystore.BaseJSONStore volttron.drivers (module), 300 method), 341 volttron.drivers.bacnet (module), 294 update() (volttron.platform.messaging.headers.Headers volttron.drivers.base (module), 296 method), 315 volttron.drivers.data_logger (module), 298 update() (volttron.platform.messaging.socket.Headers volttron.drivers.file_driver (module), 298 method), 317 volttron.drivers.modbus (module), 299 update_agent_list() (volttroncen- volttron.platform (module), 347 tral.registry.PlatformRegistry method), 256 volttron.platform.agent (module), 314 update_auth() (in module volttron.platform.control), 339 volttron.platform.agent.base (module), 301 update_by_index() (volttron.platform.auth.AuthFile volttron.platform.agent.base_historian (module), 303 method), 334 volttron.platform.agent.cron (module), 309

380 Index VOLTTRON Documentation, Release 3.5RC1 volttron.platform.agent.driven (module), 309 volttron.platform.vip.agent.subsystems.pubsub (module), volttron.platform.agent.green (module), 310 321 volttron.platform.agent.known_identities (module), 310 volttron.platform.vip.agent.subsystems.query (module), volttron.platform.agent.matching (module), 311 321 volttron.platform.agent.math_utils (module), 312 volttron.platform.vip.agent.subsystems.rpc (module), 321 volttron.platform.agent.multithreading (module), 312 volttron.platform.vip.agent.utils (module), 327 volttron.platform.agent.sched (module), 313 volttron.platform.vip.green (module), 328 volttron.platform.agent.utils (module), 314 volttron.platform.vip.router (module), 328 volttron.platform.aip (module), 330 volttron.platform.vip.socket (module), 329 volttron.platform.async (module), 332 volttron.platform.vip.tracking (module), 330 volttron.platform.auth (module), 332 volttron.platform.web (module), 346 volttron.platform.config (module), 335 volttron.utils (module), 347 volttron.platform.control (module), 337 volttron.utils.persistance (module), 347 volttron.platform.jsonrpc (module), 339 volttroncentral (module), 257 volttron.platform.keystore (module), 341 volttroncentral.agent (module), 254 volttron.platform.lib (module), 315 volttroncentral.authenticate (module), 255 volttron.platform.lib.inotify (module), 314 volttroncentral.registry (module), 255 volttron.platform.lib.inotify.green (module), 314 volttroncentral.resource_directory (module), 257 volttron.platform.lib.kwonlyargs (module), 314 volttroncentral.sessions (module), 257 volttron.platform.lib.prctl (module), 315 VolttronCentralAgent (class in volttroncentral.agent), 254 volttron.platform.main (module), 341 VolttronCentralPlatform (class in vcplatform.agent), 258 volttron.platform.messaging (module), 318 VolttronPackageWheelFileNoSign (class in volt- volttron.platform.messaging.headers (module), 315 tron.platform.packages), 343 volttron.platform.messaging.health (module), 315 volttron.platform.messaging.socket (module), 316 W volttron.platform.messaging.topics (module), 317 wait() (volttron.platform.agent.green.WaitQueue volttron.platform.messaging.utils (module), 318 method), 310 volttron.platform.packages (module), 343 wait() (volttron.platform.agent.multithreading.WaitQueue volttron.platform.packaging (module), 344 method), 313 volttron.platform.resmon (module), 345 WaitQueue (class in volttron.platform.agent.green), 310 volttron.platform.vip (module), 330 WaitQueue (class in volt- volttron.platform.vip.agent (module), 328 tron.platform.agent.multithreading), 313 volttron.platform.vip.agent.compat (module), 324 weather (module), 259 volttron.platform.vip.agent.core (module), 325 weather.settings (module), 259 volttron.platform.vip.agent.decorators (module), 326 weathertest (module), 259 volttron.platform.vip.agent.dispatch (module), 326 wheel_name (volttron.platform.packages.UnpackedPackage volttron.platform.vip.agent.errors (module), 326 attribute), 344 volttron.platform.vip.agent.example (module), 327 wheelmeta (volttron.platform.packages.UnpackedPackage volttron.platform.vip.agent.results (module), 327 attribute), 344 volttron.platform.vip.agent.subsystems (module), 322 write_property() (bacnet_proxy.agent.BACnetProxyAgent volttron.platform.vip.agent.subsystems.base (module), method), 234 318 volttron.platform.vip.agent.subsystems.channel (mod- Z ule), 319 zap_loop() (volttron.platform.auth.AuthService method), volttron.platform.vip.agent.subsystems.health (module), 335 319 ZipPackageVerifier (class in volttron.platform.packages), volttron.platform.vip.agent.subsystems.heartbeat (mod- 343 ule), 319 ZoneGui (class in mpc.CBC_Gui), 281 volttron.platform.vip.agent.subsystems.hello (module), 320 volttron.platform.vip.agent.subsystems.peerlist (module), 320 volttron.platform.vip.agent.subsystems.ping (module), 320

Index 381