This introduces a new dialplan function, DEVSTATE, which allows you to do some

pretty cool things.

First, you can get the device state of anything in the dialplan:
  NoOp(SIP/mypeer has state ${DEVSTATE(SIP/mypeer)})
  NoOp(The conference room 1234 has state ${DEVSTATE(MeetMe:1234)})

Most importantly, this allows you to create custom device states so you can
control phone lamps directly from the dialplan.
  Set(DEVSTATE(Custom:mycustomlamp)=BUSY)
  ...
  exten => mycustomlamp,hint,Custom:mycustomlamp


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@54261 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Russell Bryant
2007-02-13 22:02:20 +00:00
parent 83856d4683
commit f60efe347a
9 changed files with 327 additions and 40 deletions

View File

@@ -68,6 +68,9 @@ Changes since Asterisk 1.4-beta was branched:
* Added 'E' and 'V' commands to ExternalIVR. * Added 'E' and 'V' commands to ExternalIVR.
* Added 'DBDel' and 'DBDelTree' manager commands. * Added 'DBDel' and 'DBDelTree' manager commands.
* Added 'core show channels count' CLI command. * Added 'core show channels count' CLI command.
* Added the DEVSTATE() dialplan function which allows retrieving any device
state in the dialplan, as well as creating custom device states that are
controllable from the dialplan.
SIP changes SIP changes
----------- -----------

View File

@@ -2867,7 +2867,7 @@ static void *recordthread(void *args)
} }
/*! \brief Callback for devicestate providers */ /*! \brief Callback for devicestate providers */
static int meetmestate(const char *data) static enum ast_device_state meetmestate(const char *data)
{ {
struct ast_conference *conf; struct ast_conference *conf;
@@ -3540,12 +3540,12 @@ static int slatrunk_exec(struct ast_channel *chan, void *data)
return 0; return 0;
} }
static int sla_state(const char *data) static enum ast_device_state sla_state(const char *data)
{ {
char *buf, *station_name, *trunk_name; char *buf, *station_name, *trunk_name;
struct sla_station *station; struct sla_station *station;
struct sla_trunk_ref *trunk_ref; struct sla_trunk_ref *trunk_ref;
int res = AST_DEVICE_INVALID; enum ast_device_state res = AST_DEVICE_INVALID;
trunk_name = buf = ast_strdupa(data); trunk_name = buf = ast_strdupa(data);
station_name = strsep(&trunk_name, "_"); station_name = strsep(&trunk_name, "_");

View File

@@ -612,7 +612,7 @@ static void *changethread(void *data)
return NULL; return NULL;
} }
static int statechange_queue(const char *dev, int state, void *ign) static int statechange_queue(const char *dev, enum ast_device_state state, void *ign)
{ {
/* Avoid potential for deadlocks by spawning a new thread to handle /* Avoid potential for deadlocks by spawning a new thread to handle
the event */ the event */

205
funcs/func_devstate.c Normal file
View File

@@ -0,0 +1,205 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Digium, Inc.
*
* Russell Bryant <russell@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 Manually controlled blinky lights
*
* \author Russell Bryant <russell@digium.com>
*
* \ingroup functions
*
* \note Props go out to Ahrimanes in #asterisk for requesting this at 4:30 AM
* when I couldn't sleep. :)
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <stdlib.h>
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/utils.h"
#include "asterisk/linkedlists.h"
#include "asterisk/devicestate.h"
#include "asterisk/cli.h"
struct custom_device {
int state;
AST_RWLIST_ENTRY(custom_device) entry;
char name[1];
};
static AST_RWLIST_HEAD_STATIC(custom_devices, custom_device);
static int devstate_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
ast_copy_string(buf, ast_devstate_str(ast_device_state(data)), len);
return 0;
}
static int devstate_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
{
struct custom_device *dev;
int len = strlen("Custom:");
if (strncasecmp(data, "Custom:", len)) {
ast_log(LOG_WARNING, "The DEVSTATE function can only be used to set 'Custom:' device state!\n");
return -1;
}
data += len;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "DEVSTATE function called with no custom device name!\n");
return -1;
}
AST_RWLIST_WRLOCK(&custom_devices);
AST_RWLIST_TRAVERSE(&custom_devices, dev, entry) {
if (!strcasecmp(dev->name, data))
break;
}
if (!dev) {
if (!(dev = ast_calloc(1, sizeof(*dev) + strlen(data) + 1))) {
AST_RWLIST_UNLOCK(&custom_devices);
return -1;
}
strcpy(dev->name, data);
AST_RWLIST_INSERT_HEAD(&custom_devices, dev, entry);
}
dev->state = ast_devstate_val(value);
ast_device_state_changed("Custom:%s", dev->name);
AST_RWLIST_UNLOCK(&custom_devices);
return 0;
}
static enum ast_device_state custom_devstate_callback(const char *data)
{
struct custom_device *dev;
enum ast_device_state state = AST_DEVICE_UNKNOWN;
AST_RWLIST_RDLOCK(&custom_devices);
AST_RWLIST_TRAVERSE(&custom_devices, dev, entry) {
if (!strcasecmp(dev->name, data))
state = dev->state;
}
AST_RWLIST_UNLOCK(&custom_devices);
return state;
}
static char *cli_funcdevstate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct custom_device *dev;
switch (cmd) {
case CLI_INIT:
e->command = "funcdevstate list";
e->usage =
"Usage: funcdevstate list\n"
" List all custom device states that have been set by using\n"
" the DEVSTATE dialplan function.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != e->args)
return CLI_SHOWUSAGE;
ast_cli(a->fd, "\n"
"---------------------------------------------------------------------\n"
"--- Custom Device States --------------------------------------------\n"
"---------------------------------------------------------------------\n"
"---\n");
AST_RWLIST_RDLOCK(&custom_devices);
AST_RWLIST_TRAVERSE(&custom_devices, dev, entry) {
ast_cli(a->fd, "--- Name: 'Custom:%s' State: '%s'\n"
"---\n", dev->name, ast_devstate_str(dev->state));
}
AST_RWLIST_UNLOCK(&custom_devices);
ast_cli(a->fd,
"---------------------------------------------------------------------\n"
"---------------------------------------------------------------------\n"
"\n");
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_funcdevstate[] = {
NEW_CLI(cli_funcdevstate_list, "List currently known custom device states"),
};
static struct ast_custom_function devstate_function = {
.name = "DEVSTATE",
.synopsis = "Get or Set a device state",
.syntax = "DEVSTATE(device)",
.desc =
" The DEVSTATE function can be used to retrieve the device state from any\n"
"device state provider. For example:\n"
" NoOp(SIP/mypeer has state ${DEVSTATE(SIP/mypeer)})\n"
" NoOp(Conference number 1234 has state ${DEVSTATE(MeetMe:1234)})\n"
"\n"
" The DEVSTATE function can also be used to set custom device state from\n"
"the dialplan. The \"Custom:\" prefix must be used. For example:\n"
" Set(DEVSTATE(Custom:lamp1)=BUSY)\n"
" Set(DEVSTATE(Custom:lamp2)=NOT_INUSE)\n"
"You can subscribe to the status of a custom device state using a hint in\n"
"the dialplan:\n"
" exten => 1234,hint,Custom:lamp1\n"
"\n"
" The possible values for both uses of this function are:\n"
"UNKNOWN | NOT_INUSE | INUSE | BUSY | INVALID | UNAVAILABLE | RINGING\n"
"RINGINUSE | ONHOLD\n",
.read = devstate_read,
.write = devstate_write,
};
static int unload_module(void)
{
struct custom_device *dev;
int res = 0;
res |= ast_custom_function_unregister(&devstate_function);
res |= ast_devstate_prov_del("Custom");
res |= ast_cli_unregister_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
AST_RWLIST_WRLOCK(&custom_devices);
while ((dev = AST_RWLIST_REMOVE_HEAD(&custom_devices, entry)))
free(dev);
AST_RWLIST_UNLOCK(&custom_devices);
return res;
}
static int load_module(void)
{
int res = 0;
res |= ast_custom_function_register(&devstate_function);
res |= ast_devstate_prov_add("Custom", custom_devstate_callback);
res |= ast_cli_register_multiple(cli_funcdevstate, ARRAY_LEN(cli_funcdevstate));
return res;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Device state dialplan functions");

View File

@@ -235,7 +235,7 @@ int ast_cli_register(struct ast_cli_entry *e);
* \param e pointer to first cli entry to register * \param e pointer to first cli entry to register
* \param len number of entries to register * \param len number of entries to register
*/ */
void ast_cli_register_multiple(struct ast_cli_entry *e, int len); int ast_cli_register_multiple(struct ast_cli_entry *e, int len);
/*! \brief Unregisters a command or an array of commands /*! \brief Unregisters a command or an array of commands
* *
@@ -250,7 +250,7 @@ int ast_cli_unregister(struct ast_cli_entry *e);
* \param e pointer to first cli entry to unregister * \param e pointer to first cli entry to unregister
* \param len number of entries to unregister * \param len number of entries to unregister
*/ */
void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len); int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len);
/*! \brief Readline madness /*! \brief Readline madness
* Useful for readline, that's about it * Useful for readline, that's about it

View File

@@ -28,29 +28,42 @@
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
/*! @name DeviceStates */
/*! \@{ */ /*! Device States */
#define AST_DEVICE_UNKNOWN 0 /*!< Device is valid but channel didn't know state */ enum ast_device_state {
#define AST_DEVICE_NOT_INUSE 1 /*!< Device is not used */ AST_DEVICE_UNKNOWN, /*!< Device is valid but channel didn't know state */
#define AST_DEVICE_INUSE 2 /*!< Device is in use */ AST_DEVICE_NOT_INUSE, /*!< Device is not used */
#define AST_DEVICE_BUSY 3 /*!< Device is busy */ AST_DEVICE_INUSE, /*!< Device is in use */
#define AST_DEVICE_INVALID 4 /*!< Device is invalid */ AST_DEVICE_BUSY, /*!< Device is busy */
#define AST_DEVICE_UNAVAILABLE 5 /*!< Device is unavailable */ AST_DEVICE_INVALID, /*!< Device is invalid */
#define AST_DEVICE_RINGING 6 /*!< Device is ringing */ AST_DEVICE_UNAVAILABLE, /*!< Device is unavailable */
#define AST_DEVICE_RINGINUSE 7 /*!< Device is ringing *and* in use */ AST_DEVICE_RINGING, /*!< Device is ringing */
#define AST_DEVICE_ONHOLD 8 /*!< Device is on hold */ AST_DEVICE_RINGINUSE, /*!< Device is ringing *and* in use */
/*! \@} */ AST_DEVICE_ONHOLD, /*!< Device is on hold */
};
/*! \brief Devicestate watcher call back */ /*! \brief Devicestate watcher call back */
typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data); typedef int (*ast_devstate_cb_type)(const char *dev, enum ast_device_state state, void *data);
/*! \brief Devicestate provider call back */ /*! \brief Devicestate provider call back */
typedef int (*ast_devstate_prov_cb_type)(const char *data); typedef enum ast_device_state (*ast_devstate_prov_cb_type)(const char *data);
/*! \brief Convert device state to text string for output /*! \brief Convert device state to text string for output
* \param devstate Current device state * \param devstate Current device state
*/ */
const char *devstate2str(int devstate); const char *devstate2str(enum ast_device_state devstate);
/*! \brief Convert device state to text string that is easier to parse
* \param devstate Current device state
*/
const char *ast_devstate_str(enum ast_device_state devstate);
/*! \brief Convert device state from text to integer value
* \param The text representing the device state. Valid values are anything
* that comes after AST_DEVICE_ in one of the defined values.
* \return The AST_DEVICE_ integer value
*/
enum ast_device_state ast_devstate_val(const char *val);
/*! \brief Search the Channels by Name /*! \brief Search the Channels by Name
* \param device like a dialstring * \param device like a dialstring
@@ -59,7 +72,7 @@ const char *devstate2str(int devstate);
* Returns an AST_DEVICE_UNKNOWN if no channel found or * Returns an AST_DEVICE_UNKNOWN if no channel found or
* AST_DEVICE_INUSE if a channel is found * AST_DEVICE_INUSE if a channel is found
*/ */
int ast_parse_device_state(const char *device); enum ast_device_state ast_parse_device_state(const char *device);
/*! \brief Asks a channel for device state /*! \brief Asks a channel for device state
* \param device like a dialstring * \param device like a dialstring
@@ -69,7 +82,7 @@ int ast_parse_device_state(const char *device);
* active channels list for the device. * active channels list for the device.
* Returns an AST_DEVICE_??? state -1 on failure * Returns an AST_DEVICE_??? state -1 on failure
*/ */
int ast_device_state(const char *device); enum ast_device_state ast_device_state(const char *device);
/*! \brief Tells Asterisk the State for Device is changed /*! \brief Tells Asterisk the State for Device is changed
* \param fmt devicename like a dialstring with format parameters * \param fmt devicename like a dialstring with format parameters
@@ -115,9 +128,9 @@ int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
/*! \brief Remove device state provider /*! \brief Remove device state provider
* \param label to use in hint, like label:object * \param label to use in hint, like label:object
* \return nothing * Return -1 on failure, 0 on success
*/ */
void ast_devstate_prov_del(const char *label); int ast_devstate_prov_del(const char *label);
int ast_device_state_engine_init(void); int ast_device_state_engine_init(void);

View File

@@ -1354,20 +1354,24 @@ int ast_cli_register(struct ast_cli_entry *e)
/* /*
* register/unregister an array of entries. * register/unregister an array of entries.
*/ */
void ast_cli_register_multiple(struct ast_cli_entry *e, int len) int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
{ {
int i; int i, res = 0;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
ast_cli_register(e + i); res |= ast_cli_register(e + i);
return res;
} }
void ast_cli_unregister_multiple(struct ast_cli_entry *e, int len) int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
{ {
int i; int i, res = 0;
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
ast_cli_unregister(e + i); res |= ast_cli_unregister(e + i);
return res;
} }

View File

@@ -179,21 +179,79 @@ static ast_cond_t change_pending;
static int getproviderstate(const char *provider, const char *address); static int getproviderstate(const char *provider, const char *address);
/*! \brief Find devicestate as text message for output */ /*! \brief Find devicestate as text message for output */
const char *devstate2str(int devstate) const char *devstate2str(enum ast_device_state devstate)
{ {
return devstatestring[devstate]; return devstatestring[devstate];
} }
const char *ast_devstate_str(enum ast_device_state state)
{
const char *res = "UNKNOWN";
switch (state) {
case AST_DEVICE_UNKNOWN:
break;
case AST_DEVICE_NOT_INUSE:
res = "NOT_INUSE";
break;
case AST_DEVICE_INUSE:
res = "INUSE";
break;
case AST_DEVICE_BUSY:
res = "BUSY";
break;
case AST_DEVICE_INVALID:
res = "INVALID";
break;
case AST_DEVICE_UNAVAILABLE:
res = "UNAVAILABLE";
break;
case AST_DEVICE_RINGING:
res = "RINGING";
break;
case AST_DEVICE_RINGINUSE:
res = "RINGINUSE";
break;
case AST_DEVICE_ONHOLD:
res = "ONHOLD";
break;
}
return res;
}
enum ast_device_state ast_devstate_val(const char *val)
{
if (!strcasecmp(val, "NOT_INUSE"))
return AST_DEVICE_NOT_INUSE;
else if (!strcasecmp(val, "INUSE"))
return AST_DEVICE_INUSE;
else if (!strcasecmp(val, "BUSY"))
return AST_DEVICE_BUSY;
else if (!strcasecmp(val, "INVALID"))
return AST_DEVICE_INVALID;
else if (!strcasecmp(val, "UNAVAILABLE"))
return AST_DEVICE_UNAVAILABLE;
else if (!strcasecmp(val, "RINGING"))
return AST_DEVICE_RINGING;
else if (!strcasecmp(val, "RINGINUSE"))
return AST_DEVICE_RINGINUSE;
else if (!strcasecmp(val, "ONHOLD"))
return AST_DEVICE_ONHOLD;
return AST_DEVICE_UNKNOWN;
}
/*! \brief Find out if device is active in a call or not /*! \brief Find out if device is active in a call or not
\note find channels with the device's name in it \note find channels with the device's name in it
This function is only used for channels that does not implement This function is only used for channels that does not implement
devicestate natively devicestate natively
*/ */
int ast_parse_device_state(const char *device) enum ast_device_state ast_parse_device_state(const char *device)
{ {
struct ast_channel *chan; struct ast_channel *chan;
char match[AST_CHANNEL_NAME]; char match[AST_CHANNEL_NAME];
int res; enum ast_device_state res;
ast_copy_string(match, device, sizeof(match)-1); ast_copy_string(match, device, sizeof(match)-1);
strcat(match, "-"); strcat(match, "-");
@@ -213,12 +271,12 @@ int ast_parse_device_state(const char *device)
} }
/*! \brief Check device state through channel specific function or generic function */ /*! \brief Check device state through channel specific function or generic function */
int ast_device_state(const char *device) enum ast_device_state ast_device_state(const char *device)
{ {
char *buf; char *buf;
char *number; char *number;
const struct ast_channel_tech *chan_tech; const struct ast_channel_tech *chan_tech;
int res = 0; enum ast_device_state res = AST_DEVICE_UNKNOWN;
/*! \brief Channel driver that provides device state */ /*! \brief Channel driver that provides device state */
char *tech; char *tech;
/*! \brief Another provider of device state */ /*! \brief Another provider of device state */
@@ -281,20 +339,24 @@ int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
} }
/*! \brief Remove device state provider */ /*! \brief Remove device state provider */
void ast_devstate_prov_del(const char *label) int ast_devstate_prov_del(const char *label)
{ {
struct devstate_prov *devcb; struct devstate_prov *devcb;
int res = -1;
AST_RWLIST_WRLOCK(&devstate_provs); AST_RWLIST_WRLOCK(&devstate_provs);
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) { AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
if (!strcasecmp(devcb->label, label)) { if (!strcasecmp(devcb->label, label)) {
AST_RWLIST_REMOVE_CURRENT(&devstate_provs, list); AST_RWLIST_REMOVE_CURRENT(&devstate_provs, list);
free(devcb); free(devcb);
res = 0;
break; break;
} }
} }
AST_RWLIST_TRAVERSE_SAFE_END; AST_RWLIST_TRAVERSE_SAFE_END;
AST_RWLIST_UNLOCK(&devstate_provs); AST_RWLIST_UNLOCK(&devstate_provs);
return res;
} }
/*! \brief Get provider device state */ /*! \brief Get provider device state */

View File

@@ -284,9 +284,9 @@ static void notify_metermaids(char *exten, char *context)
} }
/*! \brief metermaids callback from devicestate.c */ /*! \brief metermaids callback from devicestate.c */
static int metermaidstate(const char *data) static enum ast_device_state metermaidstate(const char *data)
{ {
int res = AST_DEVICE_INVALID; enum ast_device_state res = AST_DEVICE_INVALID;
char *context = ast_strdupa(data); char *context = ast_strdupa(data);
char *exten; char *exten;
@@ -299,7 +299,7 @@ static int metermaidstate(const char *data)
res = ast_exists_extension(NULL, context, exten, 1, NULL); res = ast_exists_extension(NULL, context, exten, 1, NULL);
if (!res) if (res == AST_DEVICE_UNKNOWN)
return AST_DEVICE_NOT_INUSE; return AST_DEVICE_NOT_INUSE;
else else
return AST_DEVICE_INUSE; return AST_DEVICE_INUSE;