mirror of https://github.com/asterisk/asterisk
Created a data model and implemented functionality for an ARI device state resource. The following operations have been added that allow a user to manipulate an ARI controlled device: Create/Change the state of an ARI controlled device PUT /deviceStates/{deviceName}&{deviceState} Retrieve all ARI controlled devices GET /deviceStates Retrieve the current state of a device GET /deviceStates/{deviceName} Destroy a device-state controlled by ARI DELETE /deviceStates/{deviceName} The ARI controlled device must begin with 'Stasis:'. An example controlled device name would be Stasis:Example. A 'DeviceStateChanged' event has also been added so that an application can subscribe and receive device change events. Any device state, ARI controlled or not, can be subscribed to. While adding the event, the underlying subscription control mechanism was refactored so that all current and future resource subscriptions would be the same. Each event resource must now register itself in order to be able to properly handle [un]subscribes. (issue ASTERISK-22838) Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/3025/ ........ Merged revisions 403134 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@403135 65c4cc65-6c06-0410-ace0-fbb531ad65f3changes/97/197/1
parent
05cbf8df9b
commit
ed48377994
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* Kevin Harwell <kharwell@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_STASIS_APP_DEVICE_STATE_H
|
||||
#define _ASTERISK_STASIS_APP_DEVICE_STATE_H
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Stasis Application Device State API. See \ref res_stasis "Stasis
|
||||
* Application API" for detailed documentation.
|
||||
*
|
||||
* \author Kevin Harwell <kharwell@digium.com>
|
||||
* \since 12
|
||||
*/
|
||||
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/stasis_app.h"
|
||||
|
||||
/*! @{ */
|
||||
|
||||
/*!
|
||||
* \brief Convert device state to json.
|
||||
*
|
||||
* \param name the name of the device
|
||||
* \param state the device state
|
||||
* \return JSON representation.
|
||||
* \return \c NULL on error.
|
||||
*/
|
||||
struct ast_json *stasis_app_device_state_to_json(
|
||||
const char *name, enum ast_device_state state);
|
||||
|
||||
/*!
|
||||
* \brief Convert device states to json array.
|
||||
*
|
||||
* \return JSON representation.
|
||||
* \return \c NULL on error.
|
||||
*/
|
||||
struct ast_json *stasis_app_device_states_to_json(void);
|
||||
|
||||
/*! Stasis device state application result codes */
|
||||
enum stasis_device_state_result {
|
||||
/*! Application controlled device state is okay */
|
||||
STASIS_DEVICE_STATE_OK,
|
||||
/*! The device name is not application controlled */
|
||||
STASIS_DEVICE_STATE_NOT_CONTROLLED,
|
||||
/*! The application controlled device name is missing */
|
||||
STASIS_DEVICE_STATE_MISSING,
|
||||
/*! The application controlled device is unknown */
|
||||
STASIS_DEVICE_STATE_UNKNOWN,
|
||||
/*! The application controlled device has subscribers */
|
||||
STASIS_DEVICE_STATE_SUBSCRIBERS
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Changes the state of a device controlled by ARI.
|
||||
*
|
||||
* \note The controlled device must be prefixed with 'Stasis:'.
|
||||
* \note Implicitly creates the device state.
|
||||
*
|
||||
* \param name the name of the ARI controlled device
|
||||
* \param value a valid device state value
|
||||
*
|
||||
* \return a stasis device state application result.
|
||||
*/
|
||||
enum stasis_device_state_result stasis_app_device_state_update(
|
||||
const char *name, const char *value);
|
||||
|
||||
/*!
|
||||
* \brief Delete a device controlled by ARI.
|
||||
*
|
||||
* \param name the name of the ARI controlled device
|
||||
*
|
||||
* \returna stasis device state application result.
|
||||
*/
|
||||
enum stasis_device_state_result stasis_app_device_state_delete(
|
||||
const char *name);
|
||||
|
||||
/*! @} */
|
||||
|
||||
#endif /* _ASTERISK_STASIS_APP_DEVICE_STATE_H */
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* Kevin Harwell <kharwell@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief /api-docs/deviceStates.{format} implementation- Device state resources
|
||||
*
|
||||
* \author Kevin Harwell <kharwell@digium.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_device_states.h"
|
||||
#include "asterisk/stasis_app_device_state.h"
|
||||
|
||||
void ast_ari_device_states_list(
|
||||
struct ast_variable *headers,
|
||||
struct ast_ari_device_states_list_args *args,
|
||||
struct ast_ari_response *response)
|
||||
{
|
||||
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
|
||||
|
||||
if (!(json = stasis_app_device_states_to_json())) {
|
||||
ast_ari_response_error(response, 500,
|
||||
"Internal Server Error", "Error building response");
|
||||
return;
|
||||
}
|
||||
|
||||
ast_ari_response_ok(response, json);
|
||||
}
|
||||
|
||||
void ast_ari_device_states_get(struct ast_variable *headers,
|
||||
struct ast_ari_device_states_get_args *args,
|
||||
struct ast_ari_response *response)
|
||||
{
|
||||
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
|
||||
|
||||
if (!(json = stasis_app_device_state_to_json(
|
||||
args->device_name, ast_device_state(args->device_name)))) {
|
||||
ast_ari_response_error(response, 500,
|
||||
"Internal Server Error", "Error building response");
|
||||
return;
|
||||
}
|
||||
|
||||
ast_ari_response_ok(response, json);
|
||||
}
|
||||
|
||||
void ast_ari_device_states_update(struct ast_variable *headers,
|
||||
struct ast_ari_device_states_update_args *args,
|
||||
struct ast_ari_response *response)
|
||||
{
|
||||
switch (stasis_app_device_state_update(
|
||||
args->device_name, args->device_state)) {
|
||||
case STASIS_DEVICE_STATE_NOT_CONTROLLED:
|
||||
ast_ari_response_error(response, 409,
|
||||
"Conflict", "Uncontrolled device specified");
|
||||
return;
|
||||
case STASIS_DEVICE_STATE_MISSING:
|
||||
ast_ari_response_error(response, 404,
|
||||
"Not Found", "Device name is missing");
|
||||
return;
|
||||
case STASIS_DEVICE_STATE_UNKNOWN:
|
||||
ast_ari_response_error(response, 500, "Internal Server Error",
|
||||
"Unknown device");
|
||||
return;
|
||||
case STASIS_DEVICE_STATE_OK:
|
||||
case STASIS_DEVICE_STATE_SUBSCRIBERS: /* shouldn't be returned for update */
|
||||
ast_ari_response_no_content(response);
|
||||
}
|
||||
}
|
||||
|
||||
void ast_ari_device_states_delete(struct ast_variable *headers,
|
||||
struct ast_ari_device_states_delete_args *args,
|
||||
struct ast_ari_response *response)
|
||||
{
|
||||
switch (stasis_app_device_state_delete(args->device_name)) {
|
||||
case STASIS_DEVICE_STATE_NOT_CONTROLLED:
|
||||
ast_ari_response_error(response, 409,
|
||||
"Conflict", "Uncontrolled device specified");
|
||||
return;
|
||||
case STASIS_DEVICE_STATE_MISSING:
|
||||
ast_ari_response_error(response, 404,
|
||||
"Not Found", "Device name is missing");
|
||||
return;
|
||||
case STASIS_DEVICE_STATE_SUBSCRIBERS:
|
||||
ast_ari_response_error(response, 500,
|
||||
"Internal Server Error",
|
||||
"Cannot delete device with subscribers");
|
||||
return;
|
||||
case STASIS_DEVICE_STATE_OK:
|
||||
case STASIS_DEVICE_STATE_UNKNOWN:
|
||||
ast_ari_response_no_content(response);
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* Kevin Harwell <kharwell@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/ari/resource_deviceStates.c
|
||||
*
|
||||
* Device state resources
|
||||
*
|
||||
* \author Kevin Harwell <kharwell@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/ari_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_DEVICESTATES_H
|
||||
#define _ASTERISK_RESOURCE_DEVICESTATES_H
|
||||
|
||||
#include "asterisk/ari.h"
|
||||
|
||||
/*! \brief Argument struct for ast_ari_device_states_list() */
|
||||
struct ast_ari_device_states_list_args {
|
||||
};
|
||||
/*!
|
||||
* \brief List all ARI controlled device states.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void ast_ari_device_states_list(struct ast_variable *headers, struct ast_ari_device_states_list_args *args, struct ast_ari_response *response);
|
||||
/*! \brief Argument struct for ast_ari_device_states_get() */
|
||||
struct ast_ari_device_states_get_args {
|
||||
/*! \brief Name of the device */
|
||||
const char *device_name;
|
||||
};
|
||||
/*!
|
||||
* \brief Retrieve the current state of a device.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void ast_ari_device_states_get(struct ast_variable *headers, struct ast_ari_device_states_get_args *args, struct ast_ari_response *response);
|
||||
/*! \brief Argument struct for ast_ari_device_states_update() */
|
||||
struct ast_ari_device_states_update_args {
|
||||
/*! \brief Name of the device */
|
||||
const char *device_name;
|
||||
/*! \brief Device state value */
|
||||
const char *device_state;
|
||||
};
|
||||
/*!
|
||||
* \brief Change the state of a device controlled by ARI. (Note - implicitly creates the device state).
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void ast_ari_device_states_update(struct ast_variable *headers, struct ast_ari_device_states_update_args *args, struct ast_ari_response *response);
|
||||
/*! \brief Argument struct for ast_ari_device_states_delete() */
|
||||
struct ast_ari_device_states_delete_args {
|
||||
/*! \brief Name of the device */
|
||||
const char *device_name;
|
||||
};
|
||||
/*!
|
||||
* \brief Destroy a device-state controlled by ARI.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void ast_ari_device_states_delete(struct ast_variable *headers, struct ast_ari_device_states_delete_args *args, struct ast_ari_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_DEVICESTATES_H */
|
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* Kevin Harwell <kharwell@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_ari_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Device state resources
|
||||
*
|
||||
* \author Kevin Harwell <kharwell@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_ari</depend>
|
||||
<depend type="module">res_stasis</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/stasis_app.h"
|
||||
#include "ari/resource_device_states.h"
|
||||
#if defined(AST_DEVMODE)
|
||||
#include "ari/ari_model_validators.h"
|
||||
#endif
|
||||
|
||||
#define MAX_VALS 128
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /deviceStates.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void ast_ari_device_states_list_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct ast_ari_response *response)
|
||||
{
|
||||
struct ast_ari_device_states_list_args args = {};
|
||||
#if defined(AST_DEVMODE)
|
||||
int is_valid;
|
||||
int code;
|
||||
#endif /* AST_DEVMODE */
|
||||
|
||||
ast_ari_device_states_list(headers, &args, response);
|
||||
#if defined(AST_DEVMODE)
|
||||
code = response->response_code;
|
||||
|
||||
switch (code) {
|
||||
case 0: /* Implementation is still a stub, or the code wasn't set */
|
||||
is_valid = response->message == NULL;
|
||||
break;
|
||||
case 500: /* Internal Server Error */
|
||||
case 501: /* Not Implemented */
|
||||
is_valid = 1;
|
||||
break;
|
||||
default:
|
||||
if (200 <= code && code <= 299) {
|
||||
is_valid = ast_ari_validate_list(response->message,
|
||||
ast_ari_validate_device_state_fn());
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid error response %d for /deviceStates\n", code);
|
||||
is_valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_valid) {
|
||||
ast_log(LOG_ERROR, "Response validation failed for /deviceStates\n");
|
||||
ast_ari_response_error(response, 500,
|
||||
"Internal Server Error", "Response validation failed");
|
||||
}
|
||||
#endif /* AST_DEVMODE */
|
||||
|
||||
fin: __attribute__((unused))
|
||||
return;
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /deviceStates/{deviceName}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void ast_ari_device_states_get_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct ast_ari_response *response)
|
||||
{
|
||||
struct ast_ari_device_states_get_args args = {};
|
||||
struct ast_variable *i;
|
||||
#if defined(AST_DEVMODE)
|
||||
int is_valid;
|
||||
int code;
|
||||
#endif /* AST_DEVMODE */
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "deviceName") == 0) {
|
||||
args.device_name = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
ast_ari_device_states_get(headers, &args, response);
|
||||
#if defined(AST_DEVMODE)
|
||||
code = response->response_code;
|
||||
|
||||
switch (code) {
|
||||
case 0: /* Implementation is still a stub, or the code wasn't set */
|
||||
is_valid = response->message == NULL;
|
||||
break;
|
||||
case 500: /* Internal Server Error */
|
||||
case 501: /* Not Implemented */
|
||||
is_valid = 1;
|
||||
break;
|
||||
default:
|
||||
if (200 <= code && code <= 299) {
|
||||
is_valid = ast_ari_validate_device_state(
|
||||
response->message);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid error response %d for /deviceStates/{deviceName}\n", code);
|
||||
is_valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_valid) {
|
||||
ast_log(LOG_ERROR, "Response validation failed for /deviceStates/{deviceName}\n");
|
||||
ast_ari_response_error(response, 500,
|
||||
"Internal Server Error", "Response validation failed");
|
||||
}
|
||||
#endif /* AST_DEVMODE */
|
||||
|
||||
fin: __attribute__((unused))
|
||||
return;
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /deviceStates/{deviceName}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void ast_ari_device_states_update_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct ast_ari_response *response)
|
||||
{
|
||||
struct ast_ari_device_states_update_args args = {};
|
||||
struct ast_variable *i;
|
||||
#if defined(AST_DEVMODE)
|
||||
int is_valid;
|
||||
int code;
|
||||
#endif /* AST_DEVMODE */
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "deviceState") == 0) {
|
||||
args.device_state = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "deviceName") == 0) {
|
||||
args.device_name = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
ast_ari_device_states_update(headers, &args, response);
|
||||
#if defined(AST_DEVMODE)
|
||||
code = response->response_code;
|
||||
|
||||
switch (code) {
|
||||
case 0: /* Implementation is still a stub, or the code wasn't set */
|
||||
is_valid = response->message == NULL;
|
||||
break;
|
||||
case 500: /* Internal Server Error */
|
||||
case 501: /* Not Implemented */
|
||||
case 404: /* Device name is missing */
|
||||
case 409: /* Uncontrolled device specified */
|
||||
is_valid = 1;
|
||||
break;
|
||||
default:
|
||||
if (200 <= code && code <= 299) {
|
||||
is_valid = ast_ari_validate_void(
|
||||
response->message);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid error response %d for /deviceStates/{deviceName}\n", code);
|
||||
is_valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_valid) {
|
||||
ast_log(LOG_ERROR, "Response validation failed for /deviceStates/{deviceName}\n");
|
||||
ast_ari_response_error(response, 500,
|
||||
"Internal Server Error", "Response validation failed");
|
||||
}
|
||||
#endif /* AST_DEVMODE */
|
||||
|
||||
fin: __attribute__((unused))
|
||||
return;
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /deviceStates/{deviceName}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void ast_ari_device_states_delete_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct ast_ari_response *response)
|
||||
{
|
||||
struct ast_ari_device_states_delete_args args = {};
|
||||
struct ast_variable *i;
|
||||
#if defined(AST_DEVMODE)
|
||||
int is_valid;
|
||||
int code;
|
||||
#endif /* AST_DEVMODE */
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "deviceName") == 0) {
|
||||
args.device_name = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
ast_ari_device_states_delete(headers, &args, response);
|
||||
#if defined(AST_DEVMODE)
|
||||
code = response->response_code;
|
||||
|
||||
switch (code) {
|
||||
case 0: /* Implementation is still a stub, or the code wasn't set */
|
||||
is_valid = response->message == NULL;
|
||||
break;
|
||||
case 500: /* Internal Server Error */
|
||||
case 501: /* Not Implemented */
|
||||
case 404: /* Device name is missing */
|
||||
case 409: /* Uncontrolled device specified */
|
||||
is_valid = 1;
|
||||
break;
|
||||
default:
|
||||
if (200 <= code && code <= 299) {
|
||||
is_valid = ast_ari_validate_void(
|
||||
response->message);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid error response %d for /deviceStates/{deviceName}\n", code);
|
||||
is_valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_valid) {
|
||||
ast_log(LOG_ERROR, "Response validation failed for /deviceStates/{deviceName}\n");
|
||||
ast_ari_response_error(response, 500,
|
||||
"Internal Server Error", "Response validation failed");
|
||||
}
|
||||
#endif /* AST_DEVMODE */
|
||||
|
||||
fin: __attribute__((unused))
|
||||
return;
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/deviceStates.{format} */
|
||||
static struct stasis_rest_handlers deviceStates_deviceName = {
|
||||
.path_segment = "deviceName",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = ast_ari_device_states_get_cb,
|
||||
[AST_HTTP_PUT] = ast_ari_device_states_update_cb,
|
||||
[AST_HTTP_DELETE] = ast_ari_device_states_delete_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/deviceStates.{format} */
|
||||
static struct stasis_rest_handlers deviceStates = {
|
||||
.path_segment = "deviceStates",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = ast_ari_device_states_list_cb,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &deviceStates_deviceName, }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
stasis_app_ref();
|
||||
res |= ast_ari_add_handler(&deviceStates);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_ari_remove_handler(&deviceStates);
|
||||
stasis_app_unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - Device state resources",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_ari,res_stasis",
|
||||
);
|
@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* Kevin Harwell <kharwell@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/astdb.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/stasis_app_impl.h"
|
||||
#include "asterisk/stasis_app_device_state.h"
|
||||
|
||||
#define DEVICE_STATE_SIZE 64
|
||||
/*! astdb family name */
|
||||
#define DEVICE_STATE_FAMILY "StasisDeviceState"
|
||||
/*! Stasis device state provider */
|
||||
#define DEVICE_STATE_PROVIDER_STASIS "Stasis"
|
||||
/*! Scheme for custom device states */
|
||||
#define DEVICE_STATE_SCHEME_STASIS "Stasis:"
|
||||
/*! Scheme for device state subscriptions */
|
||||
#define DEVICE_STATE_SCHEME_SUB "device_state:"
|
||||
|
||||
/*! Number of hash buckets for device state subscriptions */
|
||||
#define DEVICE_STATE_BUCKETS 37
|
||||
|
||||
/*! Container for subscribed device states */
|
||||
static struct ao2_container *device_state_subscriptions;
|
||||
|
||||
/*!
|
||||
* \brief Device state subscription object.
|
||||
*/
|
||||
struct device_state_subscription {
|
||||
AST_DECLARE_STRING_FIELDS(
|
||||
AST_STRING_FIELD(app_name);
|
||||
AST_STRING_FIELD(device_name);
|
||||
);
|
||||
/*! The subscription object */
|
||||
struct stasis_subscription *sub;
|
||||
};
|
||||
|
||||
static int device_state_subscriptions_hash(const void *obj, const int flags)
|
||||
{
|
||||
const struct device_state_subscription *object;
|
||||
|
||||
switch (flags & OBJ_SEARCH_MASK) {
|
||||
case OBJ_SEARCH_OBJECT:
|
||||
object = obj;
|
||||
return ast_str_hash(object->device_name);
|
||||
case OBJ_SEARCH_KEY:
|
||||
default:
|
||||
/* Hash can only work on something with a full key. */
|
||||
ast_assert(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int device_state_subscriptions_cmp(void *obj, void *arg, int flags)
|
||||
{
|
||||
const struct device_state_subscription *object_left = obj;
|
||||
const struct device_state_subscription *object_right = arg;
|
||||
int cmp;
|
||||
|
||||
switch (flags & OBJ_SEARCH_MASK) {
|
||||
case OBJ_SEARCH_OBJECT:
|
||||
/* find objects matching both device and app names */
|
||||
if (strcmp(object_left->device_name,
|
||||
object_right->device_name)) {
|
||||
return 0;
|
||||
}
|
||||
cmp = strcmp(object_left->app_name, object_right->app_name);
|
||||
break;
|
||||
case OBJ_SEARCH_KEY:
|
||||
case OBJ_SEARCH_PARTIAL_KEY:
|
||||
ast_assert(0); /* not supported by container */
|
||||
/* fall through */
|
||||
default:
|
||||
cmp = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return cmp ? 0 : CMP_MATCH | CMP_STOP;
|
||||
}
|
||||
|
||||
static void device_state_subscription_destroy(void *obj)
|
||||
{
|
||||
struct device_state_subscription *sub = obj;
|
||||
sub->sub = stasis_unsubscribe(sub->sub);
|
||||
ast_string_field_free_memory(sub);
|
||||
}
|
||||
|
||||
static struct device_state_subscription *device_state_subscription_create(
|
||||
const struct stasis_app *app, const char *device_name)
|
||||
{
|
||||
struct device_state_subscription *sub = ao2_alloc(
|
||||
sizeof(*sub), device_state_subscription_destroy);
|
||||
const char *app_name = stasis_app_name(app);
|
||||
size_t size = strlen(device_name) + strlen(app_name) + 2;
|
||||
|
||||
if (!sub) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ast_string_field_init(sub, size)) {
|
||||
ao2_ref(sub, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_string_field_set(sub, app_name, app_name);
|
||||
ast_string_field_set(sub, device_name, device_name);
|
||||
return sub;
|
||||
}
|
||||
|
||||
static struct device_state_subscription *find_device_state_subscription(
|
||||
struct stasis_app *app, const char *name)
|
||||
{
|
||||
struct device_state_subscription dummy_sub = {
|
||||
.app_name = stasis_app_name(app),
|
||||
.device_name = name
|
||||
};
|
||||
|
||||
return ao2_find(device_state_subscriptions, &dummy_sub, OBJ_SEARCH_OBJECT);
|
||||
}
|
||||
|
||||
static void remove_device_state_subscription(
|
||||
struct device_state_subscription *sub)
|
||||
{
|
||||
ao2_unlink(device_state_subscriptions, sub);
|
||||
}
|
||||
|
||||
struct ast_json *stasis_app_device_state_to_json(
|
||||
const char *name, enum ast_device_state state)
|
||||
{
|
||||
return ast_json_pack("{s: s, s: s}",
|
||||
"name", name,
|
||||
"state", ast_devstate_str(state));
|
||||
}
|
||||
|
||||
struct ast_json *stasis_app_device_states_to_json(void)
|
||||
{
|
||||
struct ast_json *array = ast_json_array_create();
|
||||
RAII_VAR(struct ast_db_entry *, tree,
|
||||
ast_db_gettree(DEVICE_STATE_FAMILY, NULL), ast_db_freetree);
|
||||
struct ast_db_entry *entry;
|
||||
|
||||
for (entry = tree; entry; entry = entry->next) {
|
||||
const char *name = strrchr(entry->key, '/');
|
||||
if (!ast_strlen_zero(name)) {
|
||||
struct ast_str *device = ast_str_alloca(DEVICE_STATE_SIZE);
|
||||
ast_str_set(&device, 0, "%s%s",
|
||||
DEVICE_STATE_SCHEME_STASIS, ++name);
|
||||
ast_json_array_append(
|
||||
array, stasis_app_device_state_to_json(
|
||||
ast_str_buffer(device),
|
||||
ast_device_state(ast_str_buffer(device))));
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
static void send_device_state(struct device_state_subscription *sub,
|
||||
const char *name, enum ast_device_state state)
|
||||
{
|
||||
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
|
||||
|
||||
json = ast_json_pack("{s:s, s:s, s:o, s:o}",
|
||||
"type", "DeviceStateChanged",
|
||||
"application", sub->app_name,
|
||||
"timestamp", ast_json_timeval(ast_tvnow(), NULL),
|
||||
"device_state", stasis_app_device_state_to_json(
|
||||
name, state));
|
||||
|
||||
if (!json) {
|
||||
ast_log(LOG_ERROR, "Unable to create device state json object\n");
|
||||
return;
|
||||
}
|
||||
|
||||
stasis_app_send(sub->app_name, json);
|
||||
}
|
||||
|
||||
enum stasis_device_state_result stasis_app_device_state_update(
|
||||
const char *name, const char *value)
|
||||
{
|
||||
size_t size = strlen(DEVICE_STATE_SCHEME_STASIS);
|
||||
enum ast_device_state state;
|
||||
|
||||
ast_debug(3, "Updating device name = %s, value = %s", name, value);
|
||||
|
||||
if (strncasecmp(name, DEVICE_STATE_SCHEME_STASIS, size)) {
|
||||
ast_log(LOG_ERROR, "Update can only be used to set "
|
||||
"'%s' device state!\n", DEVICE_STATE_SCHEME_STASIS);
|
||||
return STASIS_DEVICE_STATE_NOT_CONTROLLED;
|
||||
}
|
||||
|
||||
name += size;
|
||||
if (ast_strlen_zero(name)) {
|
||||
ast_log(LOG_ERROR, "Update requires custom device name!\n");
|
||||
return STASIS_DEVICE_STATE_MISSING;
|
||||
}
|
||||
|
||||
if (!value || (state = ast_devstate_val(value)) == AST_DEVICE_UNKNOWN) {
|
||||
ast_log(LOG_ERROR, "Unknown device state "
|
||||
"value '%s'\n", value);
|
||||
return STASIS_DEVICE_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
ast_db_put(DEVICE_STATE_FAMILY, name, value);
|
||||
ast_devstate_changed(state, AST_DEVSTATE_CACHABLE, "%s%s",
|
||||
DEVICE_STATE_SCHEME_STASIS, name);
|
||||
|
||||
return STASIS_DEVICE_STATE_OK;
|
||||
}
|
||||
|
||||
enum stasis_device_state_result stasis_app_device_state_delete(const char *name)
|
||||
{
|
||||
const char *full_name = name;
|
||||
size_t size = strlen(DEVICE_STATE_SCHEME_STASIS);
|
||||
|
||||
if (strncasecmp(name, DEVICE_STATE_SCHEME_STASIS, size)) {
|
||||
ast_log(LOG_ERROR, "Can only delete '%s' device states!\n",
|
||||
DEVICE_STATE_SCHEME_STASIS);
|
||||
return STASIS_DEVICE_STATE_NOT_CONTROLLED;
|
||||
}
|
||||
|
||||
name += size;
|
||||
if (ast_strlen_zero(name)) {
|
||||
ast_log(LOG_ERROR, "Delete requires a device name!\n");
|
||||
return STASIS_DEVICE_STATE_MISSING;
|
||||
}
|
||||
|
||||
if (ast_device_state_clear_cache(full_name)) {
|
||||
return STASIS_DEVICE_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
ast_db_del(DEVICE_STATE_FAMILY, name);
|
||||
|
||||
/* send state change for delete */
|
||||
ast_devstate_changed(
|
||||
AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, "%s%s",
|
||||
DEVICE_STATE_SCHEME_STASIS, name);
|
||||
|
||||
return STASIS_DEVICE_STATE_OK;
|
||||
}
|
||||
|
||||
static void populate_cache(void)
|
||||
{
|
||||
RAII_VAR(struct ast_db_entry *, tree,
|
||||
ast_db_gettree(DEVICE_STATE_FAMILY, NULL), ast_db_freetree);
|
||||
struct ast_db_entry *entry;
|
||||
|
||||
for (entry = tree; entry; entry = entry->next) {
|
||||
const char *name = strrchr(entry->key, '/');
|
||||
if (!ast_strlen_zero(name)) {
|
||||
ast_devstate_changed(
|
||||
ast_devstate_val(entry->data),
|
||||
AST_DEVSTATE_CACHABLE, "%s%s\n",
|
||||
DEVICE_STATE_SCHEME_STASIS, name + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum ast_device_state stasis_device_state_cb(const char *data)
|
||||
{
|
||||
char buf[DEVICE_STATE_SIZE] = "";
|
||||
|
||||
ast_db_get(DEVICE_STATE_FAMILY, data, buf, sizeof(buf));
|
||||
|
||||
return ast_devstate_val(buf);
|
||||
}
|
||||
|
||||
static void device_state_cb(void *data, struct stasis_subscription *sub,
|
||||
struct stasis_message *msg)
|
||||
{
|
||||
struct ast_device_state_message *device_state;
|
||||
|
||||
if (ast_device_state_message_type() != stasis_message_type(msg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
device_state = stasis_message_data(msg);
|
||||
if (device_state->eid) {
|
||||
/* ignore non-aggregate states */
|
||||
return;
|
||||
}
|
||||
|
||||
send_device_state(data, device_state->device, device_state->state);
|
||||
}
|
||||
|
||||
static void *find_device_state(const struct stasis_app *app, const char *name)
|
||||
{
|
||||
return device_state_subscription_create(app, name);
|
||||
}
|
||||
|
||||
static int is_subscribed_device_state(struct stasis_app *app, const char *name)
|
||||
{
|
||||
RAII_VAR(struct device_state_subscription *, sub,
|
||||
find_device_state_subscription(app, name), ao2_cleanup);
|
||||
return sub != NULL;
|
||||
}
|
||||
|
||||
static int subscribe_device_state(struct stasis_app *app, void *obj)
|
||||
{
|
||||
struct device_state_subscription *sub = obj;
|
||||
|
||||
ast_debug(3, "Subscribing to device %s", sub->device_name);
|
||||
|
||||
if (is_subscribed_device_state(app, sub->device_name)) {
|
||||
ast_log(LOG_WARNING, "Already subscribed to %s\n", sub->device_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(sub->sub = stasis_subscribe(
|
||||
ast_device_state_topic(sub->device_name),
|
||||
device_state_cb, sub))) {
|
||||
ast_log(LOG_ERROR, "Unable to subscribe to device %s\n",
|
||||
sub->device_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ao2_link(device_state_subscriptions, sub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unsubscribe_device_state(struct stasis_app *app, const char *name)
|
||||
{
|
||||
RAII_VAR(struct device_state_subscription *, sub,
|
||||
find_device_state_subscription(app, name), ao2_cleanup);
|
||||
remove_device_state_subscription(sub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_to_json_cb(void *obj, void *arg, void *data, int flags)
|
||||
{
|
||||
struct device_state_subscription *sub = obj;
|
||||
const char *app_name = arg;
|
||||
struct ast_json *array = data;
|
||||
|
||||
if (strcmp(sub->app_name, app_name)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_json_array_append(
|
||||
array, ast_json_string_create(sub->device_name));
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void devices_to_json(const struct stasis_app *app, struct ast_json *json)
|
||||
{
|
||||
struct ast_json *array = ast_json_array_create();
|
||||
ao2_callback_data(device_state_subscriptions, OBJ_NODATA,
|
||||
device_to_json_cb, (void *)stasis_app_name(app), array);
|
||||
ast_json_object_set(json, "device_names", array);
|
||||
}
|
||||
|
||||
struct stasis_app_event_source device_state_event_source = {
|
||||
.scheme = DEVICE_STATE_SCHEME_SUB,
|
||||
.find = find_device_state,
|
||||
.subscribe = subscribe_device_state,
|
||||
.unsubscribe = unsubscribe_device_state,
|
||||
.is_subscribed = is_subscribed_device_state,
|
||||
.to_json = devices_to_json
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
populate_cache();
|
||||
if (ast_devstate_prov_add(DEVICE_STATE_PROVIDER_STASIS,
|
||||
stasis_device_state_cb)) {
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
if (!(device_state_subscriptions = ao2_container_alloc(
|
||||
DEVICE_STATE_BUCKETS, device_state_subscriptions_hash,
|
||||
device_state_subscriptions_cmp))) {
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
stasis_app_register_event_source(&device_state_event_source);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_devstate_prov_del(DEVICE_STATE_PROVIDER_STASIS);
|
||||
stasis_app_unregister_event_source(&device_state_event_source);
|
||||
ao2_cleanup(device_state_subscriptions);
|
||||
device_state_subscriptions = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis application device state support",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis");
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
global:
|
||||
LINKER_SYMBOL_PREFIXstasis_app_*;
|
||||
local:
|
||||
*;
|
||||
};
|
@ -0,0 +1,151 @@
|
||||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "Kevin Harwell <kharwell@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/deviceStates.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/deviceStates",
|
||||
"description": "Device states",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List all ARI controlled device states.",
|
||||
"nickname": "list",
|
||||
"responseClass": "List[DeviceState]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/deviceStates/{deviceName}",
|
||||
"description": "Device state",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "Retrieve the current state of a device.",
|
||||
"nickname": "get",
|
||||
"responseClass": "DeviceState",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "deviceName",
|
||||
"description": "Name of the device",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"httpMethod": "PUT",
|
||||
"summary": "Change the state of a device controlled by ARI. (Note - implicitly creates the device state).",
|
||||
"nickname": "update",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "deviceName",
|
||||
"description": "Name of the device",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "deviceState",
|
||||
"description": "Device state value",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string",
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"NOT_INUSE",
|
||||
"INUSE",
|
||||
"BUSY",
|
||||
"INVALID",
|
||||
"UNAVAILABLE",
|
||||
"RINGING",
|
||||
"RINGINUSE",
|
||||
"ONHOLD"
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Device name is missing"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Uncontrolled device specified"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"httpMethod": "DELETE",
|
||||
"summary": "Destroy a device-state controlled by ARI.",
|
||||
"nickname": "delete",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "deviceName",
|
||||
"description": "Name of the device",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Device name is missing"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Uncontrolled device specified"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"DeviceState": {
|
||||
"id": "DeviceState",
|
||||
"description": "Represents the state of a device.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the device.",
|
||||
"required": true
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"description": "Device's state",
|
||||
"required": true,
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"UNKNOWN",
|
||||
"NOT_INUSE",
|
||||
"INUSE",
|
||||
"BUSY",
|
||||
"INVALID",
|
||||
"UNAVAILABLE",
|
||||
"RINGING",
|
||||
"RINGINUSE",
|
||||
"ONHOLD"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue