Revision
16769
Author
sathieu
Date
2010-02-24 06:00:45 -0600 (Wed, 24 Feb 2010)

Log Message

Skinny: Milestone 4: Button template, Soft-key template and Soft-key sets

- buttons are now stored in the same table
- answer ButtonTemplateReqMessage with ButtonTemplateMessage: DONE
- answer SoftKeyTemplateReqMessage with SoftKeyTemplateResMessage: WIP (currently empty response)
- answer SoftKeySetReqMessage with SoftKeySetResMessage: WIP (currently empty response)

Modified Paths

Diff

Modified: freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c (16768 => 16769)


--- freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c	2010-02-24 12:00:35 UTC (rev 16768)
+++ freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c	2010-02-24 12:00:45 UTC (rev 16769)
@@ -103,33 +103,26 @@
 /*****************************************************************************/
 static char devices_sql[] =
 	"CREATE TABLE skinny_devices (\n"
-	"   device_name      VARCHAR(16),\n"
+	"   name           VARCHAR(16),\n"
 	"   user_id          INTEGER,\n"
 	"   instance         INTEGER,\n"
 	"   ip               VARCHAR(255),\n"
-	"   device_type      INTEGER,\n"
+	"   type             INTEGER,\n"
 	"   max_streams      INTEGER,\n"
 	"   port             INTEGER,\n"
 	"   codec_string     VARCHAR(255)\n"
 	");\n";
 
-static char lines_sql[] =
-	"CREATE TABLE skinny_lines (\n"
+static char buttons_sql[] =
+	"CREATE TABLE skinny_buttons (\n"
 	"   device_name      VARCHAR(16),\n"
-	"   line_position    INTEGER,\n"
-	"   line_name        VARCHAR(24),\n"
-	"   line_shortname   VARCHAR(40),\n"
-	"   line_displayname VARCHAR(44)\n"
+	"   position         INTEGER,\n"
+	"   type             VARCHAR(10),\n"
+	"   label            VARCHAR(40),\n"
+	"   value            VARCHAR(24),\n"
+	"   settings         VARCHAR(44)\n"
 	");\n";
 
-static char speeddials_sql[] =
-	"CREATE TABLE skinny_speeddials (\n"
-	"   device_name       VARCHAR(16),\n"
-	"   speed_position    INTEGER,\n"
-	"   speed_number      VARCHAR(24),\n"
-	"   speed_displayname VARCHAR(40)\n"
-	");\n";
-
 /*****************************************************************************/
 /* CHANNEL TYPES */
 /*****************************************************************************/
@@ -201,6 +194,9 @@
 	char reserved[10];
 };
 
+/* ButtonTemplateReqMessage */
+#define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
+
 #define CAPABILITIES_RES_MESSAGE 0x0010
 struct capabilities_res_message {
 	uint32_t count;
@@ -216,9 +212,15 @@
 	uint32_t alarm_param2;
 };
 
+/* SoftKeySetReqMessage */
+#define SOFT_KEY_SET_REQ_MESSAGE 0x0025
+
 /* UnregisterMessage */
 #define UNREGISTER_MESSAGE 0x0027
 
+/* SoftKeyTemplateReqMessage */
+#define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
+
 /* RegisterAvailableLinesMessage */
 #define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D
 struct register_available_lines_message {
@@ -244,6 +246,20 @@
 	char displayname[44];
 };
 
+/* ButtonTemplateMessage */
+#define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
+struct button_definition {
+	uint8_t instance_number;
+	uint8_t button_definition;
+};
+
+struct button_template_message {
+	uint32_t button_offset;
+	uint32_t button_count;
+	uint32_t total_button_count;
+	struct button_definition btn[42];
+};
+
 /* CapabilitiesReqMessage */
 #define CAPABILITIES_REQ_MESSAGE 0x009B
 
@@ -256,6 +272,36 @@
 /* KeepAliveAckMessage */
 #define KEEP_ALIVE_ACK_MESSAGE 0x0100
 
+/* SoftKeyTemplateResMessage */
+#define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
+
+struct soft_key_template_definition {
+	char soft_key_label[16];
+	uint32_t soft_key_event;
+};
+
+struct soft_key_template_res_message {
+	uint32_t soft_key_offset;
+	uint32_t soft_key_count;
+	uint32_t total_soft_key_count;
+	struct soft_key_template_definition soft_key[32];
+};
+
+/* SoftKeySetResMessage */
+#define SOFT_KEY_SET_RES_MESSAGE 0x0109
+struct soft_key_set_definition {
+	uint8_t soft_key_template_index[16];
+	uint16_t soft_key_info_index[16];
+};
+
+struct soft_key_set_res_message {
+	uint32_t soft_key_set_offset;
+	uint32_t soft_key_set_count;
+	uint32_t total_soft_key_set_count;
+	struct soft_key_set_definition soft_key_set[16];
+	uint32_t res;
+};
+
 /* Message */
 #define SKINNY_MESSAGE_FIELD_SIZE 4 /* 4-bytes field */
 #define SKINNY_MESSAGE_HEADERSIZE 12 /* three 4-bytes fields */
@@ -269,7 +315,10 @@
 	struct capabilities_res_message cap_res;
 	struct alarm_message alarm;
 	struct line_stat_res_message line_res;
+	struct button_template_message button_template;
 	struct register_rej_message reg_rej;
+	struct soft_key_template_res_message soft_key_template;
+	struct soft_key_set_res_message soft_key_set;
 	
 	uint16_t as_uint16;
 	char as_char;
@@ -1131,7 +1180,7 @@
 
 	switch_event_create_subclass(&event, event_id, subclass_name);
 	switch_assert(event);
-	if ((sql = switch_mprintf("SELECT * FROM skinny_devices WHERE device_name='%s'", listener->device_name))) {
+	if ((sql = switch_mprintf("SELECT * FROM skinny_devices WHERE name='%s'", listener->device_name))) {
 		skinny_execute_sql_callback(profile, profile->listener_mutex, sql, skinny_device_event_callback, event);
 		switch_safe_free(sql);
 	}
@@ -1159,7 +1208,7 @@
 	skinny_profile_t *profile;
 	switch_event_t *event = NULL;
 	switch_event_t *params = NULL;
-	switch_xml_t xroot, xdomain, xgroup, xuser, xskinny, xlines, xline, xspeeddials, xspeeddial;
+	switch_xml_t xroot, xdomain, xgroup, xuser, xskinny, xbuttons, xbutton;
 	char *sql;
 	assert(listener->profile);
 	profile = listener->profile;
@@ -1196,7 +1245,7 @@
 
 	if ((sql = switch_mprintf(
 			"INSERT INTO skinny_devices "
-				"(device_name, user_id, instance, ip, device_type, max_streams, codec_string) "
+				"(name, user_id, instance, ip, type, max_streams, codec_string) "
 				"VALUES ('%s','%d','%d', '%s', '%d', '%d', '%s')",
 			request->data.reg.deviceName,
 			request->data.reg.userId,
@@ -1215,46 +1264,29 @@
 
 	xskinny = switch_xml_child(xuser, "skinny");
 	if (xskinny) {
-		xlines = switch_xml_child(xskinny, "lines");
-		if (xlines) {
-			for (xline = switch_xml_child(xlines, "line"); xline; xline = xline->next) {
-				const char *position = switch_xml_attr_soft(xline, "position");
-				const char *name = switch_xml_attr_soft(xline, "name");
-				const char *shortname = switch_xml_attr_soft(xline, "shortname");
-				const char *displayname = switch_xml_attr_soft(xline, "displayname");
+		xbuttons = switch_xml_child(xskinny, "buttons");
+		if (xbuttons) {
+			for (xbutton = switch_xml_child(xbuttons, "button"); xbutton; xbutton = xbutton->next) {
+				const char *position = switch_xml_attr_soft(xbutton, "position");
+				const char *type = switch_xml_attr_soft(xbutton, "type");
+				const char *label = switch_xml_attr_soft(xbutton, "label");
+				const char *value = switch_xml_attr_soft(xbutton, "value");
+				const char *settings = switch_xml_attr_soft(xbutton, "settings");
 				if ((sql = switch_mprintf(
-						"INSERT INTO skinny_lines "
-							"(device_name, line_position, line_name, line_shortname, line_displayname) "
-							"VALUES('%s', '%s', '%s', '%s', '%s')",
+						"INSERT INTO skinny_buttons "
+							"(device_name, position, type, label, value, settings) "
+							"VALUES('%s', '%s', '%s', '%s', '%s', '%s')",
 						request->data.reg.deviceName,
 						position,
-						name,
-						shortname,
-						displayname))) {
+						type,
+						label,
+						value,
+						settings))) {
 					skinny_execute_sql(profile, sql, profile->listener_mutex);
 					switch_safe_free(sql);
 				}
 			}
 		}
-		xspeeddials = switch_xml_child(xskinny, "speed-dials");
-		if (xspeeddials) {
-			for (xspeeddial = switch_xml_child(xspeeddials, "speed-dial"); xspeeddial; xspeeddial = xspeeddial->next) {
-				const char *position = switch_xml_attr_soft(xspeeddial, "position");
-				const char *line = switch_xml_attr_soft(xspeeddial, "line");
-				const char *label = switch_xml_attr_soft(xspeeddial, "label");
-				if ((sql = switch_mprintf(
-						"INSERT INTO skinny_speeddials "
-							"(device_name, speed_position, speed_line, speed_label) "
-							"VALUES('%s', '%s', '%s', '%s')",
-						request->data.reg.deviceName,
-						position,
-						line,
-						label))) {
-					skinny_execute_sql(profile, sql, profile->listener_mutex);
-					switch_safe_free(sql);
-				}
-			}
-		}
 	}
 
 	status = SWITCH_STATUS_SUCCESS;
@@ -1336,7 +1368,7 @@
 	}
 	codec_string[string_len] = '\0';
 	if ((sql = switch_mprintf(
-			"UPDATE skinny_devices SET codec_string='%s' WHERE device_name='%s'",
+			"UPDATE skinny_devices SET codec_string='%s' WHERE name='%s'",
 			codec_string,
 			listener->device_name
 			))) {
@@ -1361,7 +1393,7 @@
 	skinny_check_data_length(request, sizeof(request->data.as_uint16));
 
 	if ((sql = switch_mprintf(
-			"UPDATE skinny_devices SET port='%d' WHERE device_name='%s'",
+			"UPDATE skinny_devices SET port='%d' WHERE name='%s'",
 			request->data.as_uint16,
 			listener->device_name
 			))) {
@@ -1371,19 +1403,146 @@
 	return SWITCH_STATUS_SUCCESS;
 }
 
-static int skinny_line_stat_request_callback(void *pArg, int argc, char **argv, char **columnNames)
+struct button_template_helper {
+	skinny_message_t *message;
+	int count[0xff+1];
+};
 
+static int skinny_handle_button_template_request_callback(void *pArg, int argc, char **argv, char **columnNames)
 {
-	skinny_message_t *message = pArg;
+	struct button_template_helper *helper = pArg;
+	skinny_message_t *message = helper->message;
+	char *device_name = argv[0];
+	int position = atoi(argv[1]);
+	char *type = argv[2];
+	int i;
+	
+	/* fill buttons between previous one and current one */
+	for(i = message->data.button_template.button_count; i+1 < position; i++) {
+		message->data.button_template.btn[i].instance_number = ++helper->count[0xff];
+		message->data.button_template.btn[i].button_definition = 0xff; /* None */
+		message->data.button_template.button_count++;
+		message->data.button_template.total_button_count++;
+	}
 
-	strcpy(message->data.line_res.name, argv[2]);
-	strcpy(message->data.line_res.shortname,  argv[3]);
-	strcpy(message->data.line_res.displayname,  argv[4]);
 
+	if (!strcasecmp(type, "line")) {
+		message->data.button_template.btn[i].instance_number = ++helper->count[0x09];
+		message->data.button_template.btn[position-1].button_definition = 0x09; /* Line */
+	} else if (!strcasecmp(type, "speed-dial")) {
+		message->data.button_template.btn[i].instance_number = ++helper->count[0x02];
+		message->data.button_template.btn[position-1].button_definition = 0x02; /* speeddial */
+	} else {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
+			"Unknown button type %s for device %s.\n", type, device_name);
+	}
+	message->data.button_template.button_count++;
+	message->data.button_template.total_button_count++;
+
 	return 0;
 }
 
+static switch_status_t skinny_handle_button_template_request(listener_t *listener, skinny_message_t *request)
+{
+	skinny_message_t *message;
+	struct button_template_helper helper = {0};
+	skinny_profile_t *profile;
+	char *sql;
 
+	switch_assert(listener->profile);
+	switch_assert(listener->device_name);
+
+	profile = listener->profile;
+
+	message = switch_core_alloc(listener->pool, 12+sizeof(message->data.button_template));
+	message->type = BUTTON_TEMPLATE_RES_MESSAGE;
+	message->length = 4 + sizeof(message->data.button_template);
+
+	message->data.button_template.button_offset = 0;
+	message->data.button_template.button_count = 0;
+	message->data.button_template.total_button_count = 0;
+	
+	helper.message = message;
+	/* Add buttons */
+	if ((sql = switch_mprintf(
+			"SELECT device_name, position, type "
+				"FROM skinny_buttons WHERE device_name='%s' ORDER BY position",
+			listener->device_name
+			))) {
+		skinny_execute_sql_callback(profile, profile->listener_mutex, sql, skinny_handle_button_template_request_callback, &helper);
+		switch_safe_free(sql);
+	}
+	
+	skinny_send_reply(listener, message);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t skinny_handle_soft_key_template_request(listener_t *listener, skinny_message_t *request)
+{
+	skinny_message_t *message;
+	skinny_profile_t *profile;
+
+	switch_assert(listener->profile);
+	switch_assert(listener->device_name);
+
+	profile = listener->profile;
+
+	message = switch_core_alloc(listener->pool, 12+sizeof(message->data.soft_key_template));
+	message->type = SOFT_KEY_TEMPLATE_RES_MESSAGE;
+	message->length = 4 + sizeof(message->data.soft_key_template);
+
+	message->data.soft_key_template.soft_key_offset = 0;
+	message->data.soft_key_template.soft_key_count = 21;
+	message->data.soft_key_template.total_soft_key_count = 21;
+	
+	/* TODO fill the template */
+	strcpy(message->data.soft_key_template.soft_key[0].soft_key_label, "\200\001");
+	message->data.soft_key_template.soft_key[0].soft_key_event = 1; /* Redial */
+	
+	skinny_send_reply(listener, message);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t skinny_handle_soft_key_set_request(listener_t *listener, skinny_message_t *request)
+{
+	skinny_message_t *message;
+	skinny_profile_t *profile;
+
+	switch_assert(listener->profile);
+	switch_assert(listener->device_name);
+
+	profile = listener->profile;
+
+	message = switch_core_alloc(listener->pool, 12+sizeof(message->data.soft_key_set));
+	message->type = SOFT_KEY_SET_RES_MESSAGE;
+	message->length = 4 + sizeof(message->data.soft_key_set);
+
+	message->data.soft_key_set.soft_key_set_offset = 0;
+	message->data.soft_key_set.soft_key_set_count = 11;
+	message->data.soft_key_set.total_soft_key_set_count = 11;
+	
+	/* TODO fill the set */
+	
+	skinny_send_reply(listener, message);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+static int skinny_line_stat_request_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+	skinny_message_t *message = pArg;
+
+	message->data.line_res.number++;
+	if (message->data.line_res.number == atoi(argv[0])) { /* wanted_position */
+		strcpy(message->data.line_res.name, argv[3]); /* value */
+		strcpy(message->data.line_res.shortname,  argv[2]); /* label */
+		strcpy(message->data.line_res.displayname,  argv[4]); /* settings */
+	}
+	return 0;
+}
+
 static switch_status_t skinny_handle_line_stat_request(listener_t *listener, skinny_message_t *request)
 {
 	skinny_message_t *message;
@@ -1400,16 +1559,19 @@
 	message = switch_core_alloc(listener->pool, 12+sizeof(message->data.line_res));
 	message->type = LINE_STAT_RES_MESSAGE;
 	message->length = 4 + sizeof(message->data.line_res);
-	message->data.line_res.number = request->data.line_req.number;
+	message->data.line_res.number = 0;
 
 	if ((sql = switch_mprintf(
-			"SELECT * FROM skinny_lines WHERE device_name='%s' AND line_position='%d'",
-			listener->device_name,
-			request->data.line_req.number
+			"SELECT '%d' AS wanted_position, position, label, value, settings "
+				"FROM skinny_buttons WHERE device_name='%s' AND type='line' "
+				"ORDER BY position",
+			request->data.line_req.number,
+			listener->device_name
 			))) {
 		skinny_execute_sql_callback(profile, profile->listener_mutex, sql, skinny_line_stat_request_callback, message);
 		switch_safe_free(sql);
 	}
+	message->data.line_res.number = request->data.line_req.number;
 	skinny_send_reply(listener, message);
 
 	return SWITCH_STATUS_SUCCESS;
@@ -1463,6 +1625,12 @@
 			return skinny_handle_capabilities_response(listener, request);
 		case PORT_MESSAGE:
 			return skinny_handle_port_message(listener, request);
+		case BUTTON_TEMPLATE_REQ_MESSAGE:
+			return skinny_handle_button_template_request(listener, request);
+		case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
+			return skinny_handle_soft_key_template_request(listener, request);
+		case SOFT_KEY_SET_REQ_MESSAGE:
+			return skinny_handle_soft_key_set_request(listener, request);
 		case LINE_STAT_REQ_MESSAGE:
 			return skinny_handle_line_stat_request(listener, request);
 		case REGISTER_AVAILABLE_LINES_MESSAGE:
@@ -1567,22 +1735,22 @@
 {
 	switch_stream_handle_t *stream = (switch_stream_handle_t *) pArg;
 
-	char *device_name = argv[0];
+	char *name = argv[0];
 	char *user_id = argv[1];
 	char *instance = argv[2];
 	char *ip = argv[3];
-	char *device_type = argv[4];
+	char *type = argv[4];
 	char *max_streams = argv[5];
 	char *port = argv[6];
 	char *codec_string = argv[7];
 
 	const char *line = "=================================================================================================";
 	stream->write_function(stream, "%s\n", line);
-	stream->write_function(stream, "DeviceName    \t%s\n", switch_str_nil(device_name));
+	stream->write_function(stream, "DeviceName    \t%s\n", switch_str_nil(name));
 	stream->write_function(stream, "UserId        \t%s\n", user_id);
 	stream->write_function(stream, "Instance      \t%s\n", instance);
 	stream->write_function(stream, "IP            \t%s\n", ip);
-	stream->write_function(stream, "DeviceType    \t%s\n", device_type);
+	stream->write_function(stream, "DeviceType    \t%s\n", type);
 	stream->write_function(stream, "MaxStreams    \t%s\n", max_streams);
 	stream->write_function(stream, "Port          \t%s\n", port);
 	stream->write_function(stream, "Codecs        \t%s\n", codec_string);
@@ -1594,7 +1762,7 @@
 static switch_status_t dump_device(skinny_profile_t *profile, const char *device_name, switch_stream_handle_t *stream)
 {
 	char *sql;
-	if ((sql = switch_mprintf("SELECT * FROM skinny_devices WHERE device_name LIKE '%s'",
+	if ((sql = switch_mprintf("SELECT * FROM skinny_devices WHERE name LIKE '%s'",
 			device_name))) {
 		skinny_execute_sql_callback(profile, profile->listener_mutex, sql, dump_device_callback, stream);
 		switch_safe_free(sql);
@@ -1992,13 +2160,11 @@
 
 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected ODBC DSN: %s\n", profile->odbc_dsn);
 					switch_odbc_handle_exec(profile->master_odbc, devices_sql, NULL, NULL);
-					switch_odbc_handle_exec(profile->master_odbc, lines_sql, NULL, NULL);
-					switch_odbc_handle_exec(profile->master_odbc, speeddials_sql, NULL, NULL);
+					switch_odbc_handle_exec(profile->master_odbc, buttons_sql, NULL, NULL);
 				} else {
 					if ((db = switch_core_db_open_file(profile->dbname))) {
 						switch_core_db_test_reactive(db, "SELECT * FROM skinny_devices", NULL, devices_sql);
-						switch_core_db_test_reactive(db, "SELECT * FROM skinny_lines", NULL, lines_sql);
-						switch_core_db_test_reactive(db, "SELECT * FROM skinny_speeddials", NULL, speeddials_sql);
+						switch_core_db_test_reactive(db, "SELECT * FROM skinny_buttons", NULL, buttons_sql);
 					} else {
 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open SQL Database!\n");
 						continue;
@@ -2007,8 +2173,7 @@
 				}
 				
 				skinny_execute_sql_callback(profile, profile->listener_mutex, "DELETE FROM skinny_devices", NULL, NULL);
-				skinny_execute_sql_callback(profile, profile->listener_mutex, "DELETE FROM skinny_lines", NULL, NULL);
-				skinny_execute_sql_callback(profile, profile->listener_mutex, "DELETE FROM skinny_speeddials", NULL, NULL);
+				skinny_execute_sql_callback(profile, profile->listener_mutex, "DELETE FROM skinny_buttons", NULL, NULL);
 
 				switch_core_hash_insert(globals.profile_hash, profile->name, profile);
 				profile = NULL;
@@ -2158,7 +2323,7 @@
 	}
 
 	if((profile = skinny_find_profile(argv[3]))) {
-		if ((sql = switch_mprintf("SELECT device_name FROM skinny_devices"))) {
+		if ((sql = switch_mprintf("SELECT name FROM skinny_devices"))) {
 			skinny_execute_sql_callback(profile, profile->listener_mutex, sql, skinny_list_devices_callback, &h);
 			switch_safe_free(sql);
 		}

Modified: freeswitch/trunk/src/mod/endpoints/mod_skinny/test-skinny.pl (16768 => 16769)


--- freeswitch/trunk/src/mod/endpoints/mod_skinny/test-skinny.pl	2010-02-24 12:00:35 UTC (rev 16768)
+++ freeswitch/trunk/src/mod/endpoints/mod_skinny/test-skinny.pl	2010-02-24 12:00:45 UTC (rev 16769)
@@ -11,7 +11,7 @@
 sub skinny_connect
 {
 	$socket = IO::Socket::INET->new(
-		PeerAddr => '192.168.0.6',
+		PeerAddr => '127.0.0.1',
 		PeerPort => 2000,
 		);
 }
@@ -62,10 +62,13 @@
 	printf ".\n";
 }
 
+# =============================================================================
+# 
+# =============================================================================
 skinny_connect();
 
-#
-skinny_send(0x0001, # register
+# =============================================================================
+skinny_send(0x0001, # Register
 	pack("a16VVVVV",
 		"SEP001120AABBCC",
 		0, # userId;
@@ -74,25 +77,42 @@
 		7, # deviceType;
 		0, # maxStreams;
 	));
-skinny_recv(); # registerack
+skinny_recv(); # RegisterAck
 
-skinny_send(0x0002, # port
+skinny_send(0x0002, # Port
 	pack("n", 2000
 	));
 
-skinny_recv(); # capreq
-skinny_send(0x0010, # capres
+skinny_send(0x002b, # HeadSetStatus
+	pack("V",
+		2, # Off
+	));
+
+skinny_recv(); # CapabilitiesReq
+skinny_send(0x0010, # CapabilitiesRes
 	pack("V"."Vva10"."Vva10",
 		2, # count
 		2, 8, "", # codec, frames, res
 		4, 16, "", # codec, frames, res
 	));
 
-skinny_send(0x000B, # linestatreq
+skinny_send(0x000e, # ButtonTemplateReqMessage
+	"");
+skinny_recv(); # ButtonTemplateMessage
+
+skinny_send(0x0028, # SoftKeyTemplateReq
+	"");
+skinny_recv(); # SoftKeyTemplateRes
+
+skinny_send(0x0025, # SoftKeySetReq
+	"");
+skinny_recv(); # SoftKeySetRes
+
+skinny_send(0x000B, # LineStatReq
 	pack("V", 1));
-skinny_recv(); # linestatres
+skinny_recv(); # LineStat
 
-skinny_send(0x002D, # registeravlines
+skinny_send(0x002D, # RegisterAvailableLines
 	pack("V", 2
 	));