parent
334c74ad39
commit
ae983ef8c8
35 changed files with 1002 additions and 221 deletions
@ -0,0 +1,391 @@ |
||||
/*
|
||||
* Voice call audio setup tool |
||||
* |
||||
* Copyright (C) 2020 Ondřej Jirman <megous@megous.com> |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
|
||||
#include <assert.h> |
||||
#include <stdlib.h> |
||||
#include <stdbool.h> |
||||
#include <stdio.h> |
||||
#include <stdarg.h> |
||||
#include <stdint.h> |
||||
#include <string.h> |
||||
#include <errno.h> |
||||
#include <unistd.h> |
||||
#include <inttypes.h> |
||||
#include <fcntl.h> |
||||
#include <sys/ioctl.h> |
||||
|
||||
#include <sound/asound.h> |
||||
#include <sound/tlv.h> |
||||
|
||||
#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) |
||||
|
||||
void syscall_error(int is_err, const char* fmt, ...) |
||||
{ |
||||
va_list ap; |
||||
|
||||
if (!is_err) |
||||
return; |
||||
|
||||
printf("ERROR: "); |
||||
va_start(ap, fmt); |
||||
vprintf(fmt, ap); |
||||
va_end(ap); |
||||
printf(": %s\n", strerror(errno)); |
||||
|
||||
exit(1); |
||||
} |
||||
|
||||
void error(const char* fmt, ...) |
||||
{ |
||||
va_list ap; |
||||
|
||||
printf("ERROR: "); |
||||
va_start(ap, fmt); |
||||
vprintf(fmt, ap); |
||||
va_end(ap); |
||||
printf("\n"); |
||||
|
||||
exit(1); |
||||
} |
||||
|
||||
struct audio_control_state { |
||||
char name[128]; |
||||
union { |
||||
int64_t i[4]; |
||||
const char* e[4]; |
||||
} vals; |
||||
bool used; |
||||
}; |
||||
|
||||
static bool audio_restore_state(struct audio_control_state* controls, int n_controls) |
||||
{ |
||||
int fd; |
||||
int ret; |
||||
|
||||
fd = open("/dev/snd/controlC0", O_CLOEXEC | O_NONBLOCK); |
||||
if (fd < 0) |
||||
error("failed to open card\n"); |
||||
|
||||
struct snd_ctl_elem_list el = { |
||||
.offset = 0, |
||||
.space = 0, |
||||
}; |
||||
ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &el); |
||||
syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_LIST failed"); |
||||
|
||||
struct snd_ctl_elem_id ids[el.count]; |
||||
el.pids = ids; |
||||
el.space = el.count; |
||||
ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &el); |
||||
syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_LIST failed"); |
||||
|
||||
for (int i = 0; i < el.used; i++) { |
||||
struct snd_ctl_elem_info inf = { |
||||
.id = ids[i], |
||||
}; |
||||
|
||||
ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &inf); |
||||
syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_INFO failed"); |
||||
|
||||
if ((inf.access & SNDRV_CTL_ELEM_ACCESS_READ) && (inf.access & SNDRV_CTL_ELEM_ACCESS_WRITE)) { |
||||
struct snd_ctl_elem_value val = { |
||||
.id = ids[i], |
||||
}; |
||||
int64_t cval = 0; |
||||
|
||||
ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_READ, &val); |
||||
syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_READ failed"); |
||||
|
||||
struct audio_control_state* cs = NULL; |
||||
for (int j = 0; j < n_controls; j++) { |
||||
if (!strcmp(controls[j].name, ids[i].name)) { |
||||
cs = &controls[j]; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!cs) { |
||||
printf("Control \"%s\" si not defined in the controls state\n", ids[i].name); |
||||
continue; |
||||
} |
||||
|
||||
cs->used = 1; |
||||
|
||||
// check if value needs changing
|
||||
|
||||
switch (inf.type) { |
||||
case SNDRV_CTL_ELEM_TYPE_BOOLEAN: |
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER: |
||||
for (int j = 0; j < inf.count; j++) { |
||||
if (cs->vals.i[j] != val.value.integer.value[j]) { |
||||
// update
|
||||
//printf("%s <=[%d]= %"PRIi64"\n", ids[i].name, j, cs->vals.i[j]);
|
||||
|
||||
val.value.integer.value[j] = cs->vals.i[j]; |
||||
ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &val); |
||||
syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_WRITE failed"); |
||||
} |
||||
} |
||||
|
||||
break; |
||||
case SNDRV_CTL_ELEM_TYPE_INTEGER64: |
||||
for (int j = 0; j < inf.count; j++) { |
||||
if (cs->vals.i[j] != val.value.integer64.value[j]) { |
||||
// update
|
||||
//printf("%s <=[%d]= %"PRIi64"\n", ids[i].name, j, cs->vals.i[j]);
|
||||
|
||||
val.value.integer64.value[j] = cs->vals.i[j]; |
||||
ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &val); |
||||
syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_WRITE failed"); |
||||
} |
||||
} |
||||
|
||||
break; |
||||
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED: { |
||||
for (int k = 0; k < inf.count; k++) { |
||||
int eval = -1; |
||||
for (int j = 0; j < inf.value.enumerated.items; j++) { |
||||
inf.value.enumerated.item = j; |
||||
|
||||
ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &inf); |
||||
syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_INFO failed"); |
||||
|
||||
if (!strcmp(cs->vals.e[k], inf.value.enumerated.name)) { |
||||
eval = j; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (eval < 0) |
||||
error("enum value %s not found\n", cs->vals.e[k]); |
||||
|
||||
if (eval != val.value.enumerated.item[k]) { |
||||
// update
|
||||
//printf("%s <=%d= %s\n", ids[i].name, k, cs->vals.e[k]);
|
||||
|
||||
val.value.enumerated.item[k] = eval; |
||||
ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &val); |
||||
syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_WRITE failed"); |
||||
} |
||||
} |
||||
|
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (int j = 0; j < n_controls; j++) |
||||
if (!controls[j].used) |
||||
printf("Control \"%s\" is defined in state but not present on the card\n", controls[j].name); |
||||
|
||||
close(fd); |
||||
return true; |
||||
} |
||||
|
||||
struct audio_setup { |
||||
bool mic_on; |
||||
bool spk_on; |
||||
bool hp_on; |
||||
bool ear_on; |
||||
|
||||
// when sending audio to modem from AIF1 R, also play that back
|
||||
// to me locally (just like AIF1 L plays just to me)
|
||||
//
|
||||
// this is to monitor what SW is playing to the modem (so that
|
||||
// I can hear my robocaller talking)
|
||||
bool modem_playback_monitor; |
||||
|
||||
// enable modem routes to DAC/from ADC (spk/mic)
|
||||
// digital paths to AIF1 are always on
|
||||
bool to_modem_on; |
||||
bool from_modem_on; |
||||
|
||||
// shut off/enable all digital paths to the modem:
|
||||
// keep this off until the call starts, then turn it on
|
||||
bool dai2_en; |
||||
|
||||
int mic_gain; |
||||
int spk_vol; |
||||
int ear_vol; |
||||
int hp_vol; |
||||
}; |
||||
|
||||
static void audio_set_controls(struct audio_setup* s) |
||||
{ |
||||
struct audio_control_state controls[] = { |
||||
//
|
||||
// Analog input:
|
||||
//
|
||||
|
||||
// Mic 1 (daughterboard)
|
||||
{ .name = "Mic1 Boost Volume", .vals.i = { s->mic_gain } }, |
||||
|
||||
// Mic 2 (headphones)
|
||||
{ .name = "Mic2 Boost Volume", .vals.i = { 0 } }, |
||||
|
||||
// Line in (unused on PP)
|
||||
// no controls yet
|
||||
|
||||
// Input mixers before ADC
|
||||
|
||||
{ .name = "Mic1 Capture Switch", .vals.i = { !!s->mic_on, !!s->mic_on } }, |
||||
{ .name = "Mic2 Capture Switch", .vals.i = { 0, 0 } }, |
||||
{ .name = "Line In Capture Switch", .vals.i = { 0, 0 } }, // Out Mix -> In Mix
|
||||
{ .name = "Mixer Capture Switch", .vals.i = { 0, 0 } }, |
||||
{ .name = "Mixer Reversed Capture Switch", .vals.i = { 0, 0 } }, |
||||
|
||||
// ADC
|
||||
{ .name = "ADC Gain Capture Volume", .vals.i = { 0 } }, |
||||
{ .name = "ADC Capture Volume", .vals.i = { 160, 160 } }, // digital gain
|
||||
|
||||
//
|
||||
// Digital paths:
|
||||
//
|
||||
|
||||
// AIF1 (SoC)
|
||||
|
||||
// AIF1 slot0 capture mixer sources
|
||||
{ .name = "AIF1 AD0 Mixer ADC Capture Switch", .vals.i = { 1, 0 } }, |
||||
{ .name = "AIF1 AD0 Mixer AIF1 DA0 Capture Switch", .vals.i = { 0, 0 } }, |
||||
{ .name = "AIF1 AD0 Mixer AIF2 DAC Capture Switch", .vals.i = { 0, 1 } }, |
||||
{ .name = "AIF1 AD0 Mixer AIF2 DAC Rev Capture Switch", .vals.i = { 0, 0 } }, //XXX: capture right from the left AIF2?
|
||||
{ .name = "AIF1 Loopback Switch", .vals.i = { 0 } }, |
||||
|
||||
// AIF1 slot0 capture/playback mono mixing/digital volume
|
||||
{ .name = "AIF1 AD0 Capture Volume", .vals.i = { 160, 160 } }, |
||||
{ .name = "AIF1 AD0 Stereo Capture Route", .vals.e = { "Stereo", "Stereo" } }, |
||||
{ .name = "AIF1 DA0 Playback Volume", .vals.i = { 160, 160 } }, |
||||
{ .name = "AIF1 DA0 Stereo Playback Route", .vals.e = { "Stereo", "Stereo" } }, |
||||
|
||||
// AIF2 (modem)
|
||||
|
||||
// AIF2 capture mixer sources
|
||||
{ .name = "AIF2 ADC Mixer ADC Capture Switch", .vals.i = { !!s->to_modem_on && !!s->dai2_en, 0 } }, // from adc/mic
|
||||
{ .name = "AIF2 ADC Mixer AIF1 DA0 Capture Switch", .vals.i = { 0, 1 } }, // from aif1 R
|
||||
{ .name = "AIF2 ADC Mixer AIF2 DAC Rev Capture Switch", .vals.i = { 0, 0 } }, |
||||
{ .name = "AIF2 Loopback Switch", .vals.i = { 0 } }, |
||||
|
||||
// AIF2 capture/playback mono mixing/digital volume
|
||||
{ .name = "AIF2 ADC Capture Volume", .vals.i = { 160, 160 } }, |
||||
{ .name = "AIF2 DAC Playback Volume", .vals.i = { 160, 160 } }, |
||||
{ .name = "AIF2 ADC Stereo Capture Route", .vals.e = { "Mix Mono", "Mix Mono" } }, // we mix because we're sending two channels (from mic and AIF1 R)
|
||||
{ .name = "AIF2 DAC Stereo Playback Route", .vals.e = { "Sum Mono", "Sum Mono" } }, // we sum because modem is sending a single channel
|
||||
|
||||
// AIF3 (bluetooth)
|
||||
|
||||
{ .name = "AIF3 Loopback Switch", .vals.i = { 0 } }, |
||||
{ .name = "AIF3 ADC Capture Route", .vals.e = { "None" } }, |
||||
{ .name = "AIF3 DAC Playback Route", .vals.e = { "None" } }, |
||||
|
||||
// DAC
|
||||
|
||||
// DAC input mixers (sources from ADC, and AIF1/2)
|
||||
{ .name = "DAC Mixer ADC Playback Switch", .vals.i = { 0, 0 } }, // we don't play our mic to ourselves
|
||||
{ .name = "DAC Mixer AIF1 DA0 Playback Switch", .vals.i = { 1, !!s->modem_playback_monitor } }, |
||||
{ .name = "DAC Mixer AIF2 DAC Playback Switch", .vals.i = { 0, !!s->dai2_en && !!s->from_modem_on } }, |
||||
|
||||
//
|
||||
// Analog output:
|
||||
//
|
||||
|
||||
// Output mixer after DAC
|
||||
|
||||
{ .name = "DAC Playback Switch", .vals.i = { 1, 1 } }, |
||||
{ .name = "DAC Reversed Playback Switch", .vals.i = { 1, 1 } }, |
||||
{ .name = "DAC Playback Volume", .vals.i = { 160, 160 } }, |
||||
{ .name = "Mic1 Playback Switch", .vals.i = { 0, 0 } }, |
||||
{ .name = "Mic1 Playback Volume", .vals.i = { 0 } }, |
||||
{ .name = "Mic2 Playback Switch", .vals.i = { 0, 0 } }, |
||||
{ .name = "Mic2 Playback Volume", .vals.i = { 0 } }, |
||||
{ .name = "Line In Playback Switch", .vals.i = { 0, 0 } }, |
||||
{ .name = "Line In Playback Volume", .vals.i = { 0 } }, |
||||
|
||||
// Outputs
|
||||
|
||||
{ .name = "Earpiece Source Playback Route", .vals.e = { "Left Mixer" } }, |
||||
{ .name = "Earpiece Playback Switch", .vals.i = { !!s->ear_on } }, |
||||
{ .name = "Earpiece Playback Volume", .vals.i = { s->ear_vol } }, |
||||
|
||||
{ .name = "Headphone Source Playback Route", .vals.e = { "Mixer", "Mixer" } }, |
||||
{ .name = "Headphone Playback Switch", .vals.i = { !!s->hp_on, !!s->hp_on } }, |
||||
{ .name = "Headphone Playback Volume", .vals.i = { s->hp_vol } }, |
||||
|
||||
// Loudspeaker
|
||||
{ .name = "Line Out Source Playback Route", .vals.e = { "Mono Differential", "Mono Differential" } }, |
||||
{ .name = "Line Out Playback Switch", .vals.i = { !!s->spk_on, !!s->spk_on } }, |
||||
{ .name = "Line Out Playback Volume", .vals.i = { s->spk_vol } }, |
||||
}; |
||||
|
||||
audio_restore_state(controls, ARRAY_SIZE(controls)); |
||||
} |
||||
|
||||
static struct audio_setup audio_setup = { |
||||
.mic_on = false, |
||||
.ear_on = false, |
||||
.spk_on = false, |
||||
.hp_on = false, |
||||
|
||||
.from_modem_on = true, |
||||
.to_modem_on = true, |
||||
.modem_playback_monitor = false, |
||||
|
||||
.dai2_en = false, |
||||
|
||||
.hp_vol = 15, |
||||
.spk_vol = 15, |
||||
.ear_vol = 31, |
||||
.mic_gain = 1, |
||||
}; |
||||
|
||||
int main(int ac, char* av[]) |
||||
{ |
||||
int opt; |
||||
|
||||
while ((opt = getopt(ac, av, "smhe2")) != -1) { |
||||
switch (opt) { |
||||
case 's': |
||||
audio_setup.spk_on = 1; |
||||
break; |
||||
case 'm': |
||||
audio_setup.mic_on = 1; |
||||
break; |
||||
case 'h': |
||||
audio_setup.hp_on = 1; |
||||
break; |
||||
case 'e': |
||||
audio_setup.ear_on = 1; |
||||
break; |
||||
case 'z': |
||||
audio_setup.modem_playback_monitor = 1; |
||||
break; |
||||
case '2': |
||||
audio_setup.dai2_en = 1; |
||||
break; |
||||
default: /* '?' */ |
||||
fprintf(stderr, "Usage: %s [-s] [-m] [-h] [-e] [-2]\n", av[0]); |
||||
exit(EXIT_FAILURE); |
||||
} |
||||
} |
||||
|
||||
audio_set_controls(&audio_setup); |
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1,26 @@ |
||||
#include <gammu.h> |
||||
#include <gammu-message.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
int main(int argc, char **argv) { |
||||
GSM_SMSMessage m; |
||||
GSM_Error err; |
||||
int i; |
||||
char timestamp[50]; |
||||
char * hexstring; |
||||
|
||||
hexstring = malloc(strlen(argv[1]) / 2); |
||||
for (i = 0; i < strlen(argv[1]) / 2; i++) { |
||||
sscanf(argv[1] + i*2, "%2hhx", &hexstring[i]); |
||||
} |
||||
err = GSM_DecodePDUFrame(NULL, &m, hexstring, strlen(argv[1]) / 2, NULL, 1); |
||||
if (err != ERR_NONE) { |
||||
fprintf(stderr, "Failure to parse string: %s\n", GSM_ErrorString(err)); |
||||
} |
||||
GSM_DateTimeToTimestamp(&m.DateTime, timestamp); |
||||
|
||||
printf("Date: %s\n", timestamp); |
||||
printf("Number: %s\n", DecodeUnicodeConsole(m.Number)); |
||||
printf("Message:\n%s\n", DecodeUnicodeConsole(m.Text)); |
||||
} |
@ -0,0 +1,126 @@ |
||||
#!/usr/bin/env sh |
||||
|
||||
err() { |
||||
echo $1 | dmenu -fn Terminus-20 -c -l 10 |
||||
exit 1 |
||||
} |
||||
|
||||
toggleflag() { |
||||
TOGGLEFLAG=$1 |
||||
shift |
||||
FLAGS="$@" |
||||
|
||||
echo -- "$FLAGS" | grep -- "$TOGGLEFLAG" >&2 && |
||||
NEWFLAGS="$(echo -- "$FLAGS" | sed "s/$TOGGLEFLAG//g")" || |
||||
NEWFLAGS="$(echo -- "$FLAGS $TOGGLEFLAG")" |
||||
|
||||
NEWFLAGS="$(echo -- "$NEWFLAGS" | sed "s/--//g; s/ / /g")" |
||||
|
||||
sxmo_megiaudioroute $NEWFLAGS |
||||
echo -- $NEWFLAGS |
||||
} |
||||
|
||||
dialmenu() { |
||||
NUMS=$(ls -1 ~/.sxmo || "") |
||||
NUMBER="$( |
||||
echo -e "$NUMS\nTest Number 804-222-1111" | |
||||
dmenu -l 10 -p Number -c -fn Terminus-20 | |
||||
awk -F' ' '{print $NF}' | |
||||
tr -d - |
||||
)" |
||||
echo "Attempting to dial: $NUMBER" >&2 |
||||
VID=$( |
||||
sudo mmcli -m 0 --voice-create-call "number=$NUMBER" | grep -Eo Call/[0-9]+ | grep -oE [0-9]+ |
||||
) |
||||
echo "Starting call with VID: $VID" >&2 |
||||
|
||||
echo "$(startcall $VID)" |
||||
} |
||||
|
||||
startcall() { |
||||
VID=$1 |
||||
sudo mmcli -m 0 -o $VID --start | grep "successfully started" || err "Couldn't start call!" |
||||
echo $VID |
||||
} |
||||
|
||||
hangup() { |
||||
VID=$1 |
||||
sudo mmcli -m 0 -o $VID --hangup |
||||
exit 1 |
||||
} |
||||
|
||||
incallmenu() { |
||||
DMENUIDX=0 |
||||
VID="$1" |
||||
|
||||
# E.g. Run once w/o -2, and then run once with -2 |
||||
FLAGS="-e -m" |
||||
sxmo_megiaudioroute $FLAGS |
||||
FLAGS="$FLAGS -2" |
||||
sxmo_megiaudioroute $FLAGS |
||||
|
||||
while true |
||||
do |
||||
echo -- "$FLAGS" | grep -- "-m" && TMUTE="Mute" || TMUTE="Unmute" |
||||
echo -- "$FLAGS" | grep -- "-z" && TECHO="Echomic Off" || TECHO="Echomic On" |
||||
echo -- "$FLAGS" | grep -- "-e" && TEARPIECE="Earpiece Off" || TEARPIECE="Earpiece On" |
||||
echo -- "$FLAGS" | grep -- "-h" && TLINEJACK="Linejack Off" || TLINEJACK="Linejack On" |
||||
echo -- "$FLAGS" | grep -- "-s" && TSPEAKER="Speakerphone Off" || TSPEAKER="Speakerphone On" |
||||
|
||||
CHOICES="$(echo -ne ' |
||||
Volume ↑ ^ sxmo_vol.sh up |
||||
Volume ↓ ^ sxmo_vol.sh down |
||||
TMUTE ^ FLAGS="$(toggleflag "-m" "$FLAGS")" |
||||
TECHO ^ FLAGS="$(toggleflag "-z" "$FLAGS")" |
||||
TEARPIECE ^ FLAGS="$(toggleflag "-e" "$FLAGS")" |
||||
TLINEJACK ^ FLAGS="$(toggleflag "-h" "$FLAGS")" |
||||
TSPEAKER ^ FLAGS="$(toggleflag "-s" "$FLAGS")" |
||||
DTMF Tones ^ dtmfmenu $VID |
||||
Hangup ^ hangup $VID |
||||
Lock Screen ^ hangup |
||||
' | sed "s/TMUTE/$TMUTE/;s/TECHO/$TECHO/;s/TEARPIECE/$TEARPIECE/;s/TLINEJACK/$TLINEJACK/;s/TSPEAKER/$TSPEAKER/" |
||||
)" |
||||
|
||||
PICKED=$( |
||||
echo "$CHOICES" | |
||||
xargs -0 echo | |
||||
cut -d'^' -f1 | |
||||
sed '/^[[:space:]]*$/d' | |
||||
awk '{$1=$1};1' | |
||||
dmenu -idx $DMENUIDX -l 14 -c -fn "Terminus-30" -p "$VID" |
||||
) |
||||
CMD=$(echo "$CHOICES" | grep "$PICKED" | cut -d '^' -f2) |
||||
DMENUIDX=$(echo $(echo "$CHOICES" | grep -n "$PICKED" | cut -d ':' -f1) - 1 | bc) |
||||
eval $CMD |
||||
done |
||||
} |
||||
|
||||
dtmfmenu() { |
||||
VID=$1 |
||||
DMENUIDX=0 |
||||
NUMS="0123456789*#ABCD" |
||||
|
||||
while true |
||||
do |
||||
PICKED="$( |
||||
echo $NUMS | grep -o . | sed '1 iReturn to Call Menu' | |
||||
dmenu -l 20 -fn Terminus-20 -c -idx $DMENUIDX -p "DTMF Tone" |
||||
)" |
||||
DMENUIDX=$(echo $NUMS | grep -bo $PICKED | cut -d: -f1 | xargs -IN echo 2+N | bc) |
||||
|
||||
echo $PICKED | grep "Return to Call Menu" && break |
||||
mmcli -m 0 -o $VID --send-dtmf="$PICKED" |
||||
done |
||||
} |
||||
|
||||
|
||||
dial() { |
||||
VID="$(dialmenu)" |
||||
incallmenu $VID |
||||
} |
||||
|
||||
pickup() { |
||||
startcall $1 |
||||
} |
||||
|
||||
$@ |
@ -0,0 +1,57 @@ |
||||
#!/usr/bin/env sh |
||||
TIMEOUT=3 |
||||
MODEM=$(mmcli -L | grep -o "Modem/[0-9]" | grep -o [0-9]$) |
||||
|
||||
newcall() { |
||||
sxmo_setpineled green 1 |
||||
|
||||
echo "Incoming Call:" |
||||
INCOMINGNUMBER=$( |
||||
mmcli -m 0 --voice-list-calls -o 3 -K | |
||||
grep call.properties.number | |
||||
cut -d ':' -f 2 |
||||
) |
||||
echo "Number: $INCOMINGNUMBER" |
||||
} |
||||
|
||||
newtexts() { |
||||
sxmo_setpineled green 1 |
||||
|
||||
echo "New Texts:" |
||||
for i in $(echo -e "$1") ; do |
||||
DAT="$(mmcli -m 0 -s $i -K)" |
||||
|
||||
TEXT="$(echo "$DAT" | grep sms.content.text | sed -E 's/^sms\.content\.text\s+:\s+//')" |
||||
NUM="$(echo "$DAT" | grep sms.content.number | sed -E 's/^sms\.content\.number\s+:\s+[+]?//')" |
||||
TIME="$(echo "$DAT" | grep sms.properties.timestamp | sed -E 's/^sms\.properties\.timestamp\s+:\s+//')" |
||||
TEXTSIZE="$(echo $TEXT | wc -c)" |
||||
|
||||
mkdir -p ~/.sxmo/$NUM |
||||
echo -ne "$NUM at $TIME:\n$TEXT\n\n" >> ~/.sxmo/$NUM/sms.txt |
||||
echo -ne "$TIME\trecv_txt\t$NUM\t$TEXTSIZE chars\n" >> ~/.sxmo/$NUM/log.tsv |
||||
sudo mmcli -m $MODEM --messaging-delete-sms=$i |
||||
done |
||||
} |
||||
|
||||
while true |
||||
do |
||||
sxmo_setpineled green 0 |
||||
VOICECALLID="$( |
||||
mmcli -m 0 --voice-list-calls -a | |
||||
grep -Eo '[0-9]+ incoming \(ringing-in\)' | |
||||
grep -Eo '[0-9]+' |
||||
)" |
||||
|
||||
TEXTIDS="$( |
||||
mmcli -m 0 --messaging-list-sms | |
||||
grep -Eo '/SMS/[0-9] \(received\)' | |
||||
grep -Eo '[0-9]+' |
||||
)" |
||||
|
||||
echo VIDS $VOICECALLID |
||||
echo TIDS $TEXTIDS |
||||
|
||||
echo "$VOICECALLID" | grep . && newcall "$VOICECALLID" |
||||
echo "$TEXTIDS" | grep . && newtexts "$TEXTIDS" |
||||
sleep $TIMEOUT |
||||
done |
@ -0,0 +1,53 @@ |
||||
#!/usr/bin/env sh |
||||
EDITOR=vis |
||||
|
||||
modem() { |
||||
mmcli -L | grep -o "Modem/[0-9]" | grep -o [0-9]$ |
||||
} |
||||
|
||||
editmsg() { |
||||
TMP="$(mktemp --suffix $1_msg)" |
||||
echo "$2" > "$TMP" |
||||
TEXT="$(st -e $EDITOR $TMP)" |
||||
cat $TMP |
||||
} |
||||
|
||||
sendmsg() { |
||||
MODEM=$(modem) |
||||
SMSNO=$( |
||||
sudo mmcli -m $MODEM --messaging-create-sms="text='$2',number=$1" | |
||||
grep -o [0-9]*$ |
||||
) |
||||
sudo mmcli -s ${SMSNO} --send |
||||
for i in $(mmcli -m $MODEM --messaging-list-sms | grep " (sent)" | cut -f5 -d' ') ; do |
||||
sudo mmcli -m $MODEM --messaging-delete-sms=$i |
||||
done |
||||
} |
||||
|
||||
main() { |
||||
modem || st -e "echo Couldn't determine modem number - is modem online?" |
||||
|
||||
# Prompt for number |
||||
NUMBER=$( |
||||
echo -e "Enter Number: \nCancel" | |
||||
dmenu -p "Number" -fn "Terminus-20" -l 10 -c |
||||
) |
||||
echo "$NUMBER" | grep -E "^Cancel$" && exit 1 |
||||
|
||||
# Compose first version of msg |
||||
TEXT="$(editmsg $NUMBER 'Enter text message here')" |
||||
|
||||
while true |
||||
do |
||||
CHARS=$(echo "$TEXT" | wc -c) |
||||
CONFIRM=$( |
||||
echo -e "Message ($CHARS) to -> $NUMBER: ($TEXT)\nEdit\nSend\nCancel" | |
||||
dmenu -c -idx 1 -p "Confirm" -fn "Terminus-20" -l 10 |
||||
) |
||||
echo "$CONFIRM" | grep -E "^Send$" && sendmsg "$NUMBER" "$TEXT" && exit 0 |
||||
echo "$CONFIRM" | grep -E "^Cancel$" && exit 1 |
||||
echo "$CONFIRM" | grep -E "^Edit$" && TEXT="$(editmsg "$NUMBER" "$TEXT")" |
||||
done |
||||
} |
||||
|
||||
main |
@ -0,0 +1,145 @@ |
||||
#!/usr/bin/expect -f |
||||
|
||||
proc setup {} { |
||||
set timeout 5 |
||||
exp_internal 1 |
||||
set ::env(DISPLAY) :0 |
||||
spawn sh -c "screen -rx dialer || screen -S dialer /dev/ttyUSB2 115200" |
||||
set modem_pid $spawn_id |
||||
sleep 0.2 |
||||
send -i $modem_pid "AT+QDAI=1,0,0,2,0,1,1,1;\r" |
||||
expect { |
||||
-i $modem_pid "OK" { return $modem_pid } |
||||
} |
||||
puts "Couldn't establish connection to modem?" |
||||
exit |
||||
} |
||||
|
||||
proc dialermenu {} { |
||||
# Prompt user for number to dial |
||||
spawn sh -c "pidof svkbd-sxmo || svkbd-sxmo" |
||||
spawn sh -c " |
||||
echo Test Number 804-222-1111 | |
||||
dmenu -l 10 -p Number -c -fn Terminus-20 | |
||||
awk -F' ' '{print \$NF}' | |
||||
tr -d - |
||||
" |
||||
wait |
||||
expect -re "(\\d+)" |
||||
return $expect_out(buffer) |
||||
} |
||||
|
||||
proc call {modem_pid number} { |
||||
puts "Calling <$number>" |
||||
send -i $modem_pid "ATD$number;\r" |
||||
expect { |
||||
-i $modem_pid "OK" { incallmenu $modem_pid $number } |
||||
} |
||||
spawn sh -c "echo 'Failed to connect?' | dmenu -fn Terminus-20 -c" |
||||
wait |
||||
exit |
||||
} |
||||
|
||||
proc lremove {l p} { |
||||
set p [lsearch -exact $l $p] |
||||
return "[lrange $l 0 $p-1] [lrange $l $p+1 end]" |
||||
} |
||||
proc iteminlist {l i} { |
||||
return [expr [lsearch -exact $l $i] >= 0] |
||||
} |
||||
|
||||
proc applyaudiorouting {flags} { |
||||
puts "Applying audio routing flags: $flags" |
||||
spawn sxmo_megiaudioroute {*}$flags |
||||
wait |
||||
} |
||||
|
||||
proc incallmenu {modem_pid number} { |
||||
# E.g. default earpiece to listen and mic to speak |
||||
set flags { -e -m } |
||||
applyaudiorouting $flags |
||||
lappend flags -2 |
||||
applyaudiorouting $flags |
||||
|
||||
while 1 { |
||||
set mutetext [expr {[iteminlist $flags -m] ? "Mute" : "Unmute"}] |
||||
set echotext [expr {[iteminlist $flags -z] ? "Echomic Off" : "Echomic On"}] |
||||
set earpiecetext [expr {[iteminlist $flags -e] ? "Earpiece Off" : "Earpiece On"}] |
||||
set headphonetext [expr {[iteminlist $flags -h] ? "Linejack Off" : "Linejack On"}] |
||||
set speakerphonetext [expr {[iteminlist $flags -s] ? "Speakerphone Off" : "Speakerphone On"}] |
||||
spawn sh -c " |
||||
echo -e ' |
||||
Volume ↑ |
||||
Volume ↓ |
||||
$mutetext |
||||
$echotext |
||||
$earpiecetext |
||||
$headphonetext |
||||
$speakerphonetext |
||||
Windowify |
||||
Hangup |
||||
Lock Screen |
||||
Number Input |
||||
' | sed -r '/^\s*$/d' | awk '{\$1=\$1};1' | dmenu -c -fn Terminus-20 -l 10 -p '$number' |
||||
" |
||||
wait |
||||
expect { |
||||
"Mute" { set flags [lremove $flags -m]; applyaudiorouting $flags } |
||||
"Unmute" { lappend flags -m; applyaudiorouting $flags } |
||||
|
||||
"Echomic Off" { set flags [lremove $flags -z]; applyaudiorouting $flags } |
||||
"Echomic On" { lappend flags -z; applyaudiorouting $flags } |
||||
|
||||
"Earpiece Off" { set flags [lremove $flags -e]; applyaudiorouting $flags } |
||||
"Earpiece On" { lappend flags -e; applyaudiorouting $flags } |
||||
|
||||
"Linejack Off" { set flags [lremove $flags -h]; applyaudiorouting $flags } |
||||
"Linejack On" { lappend flags -h; applyaudiorouting $flags } |
||||
|
||||
"Speakerphone Off" { set flags [lremove $flags -s]; applyaudiorouting $flags } |
||||
"Speakerphone On" { lappend flags -s; applyaudiorouting $flags } |
||||
|
||||
"Volume ↑" { spawn sxmo_vol.sh up } |
||||
"Volume ↓" { spawn sxmo_vol.sh down } |
||||
|
||||
"Numberpad" { spawn sxmo_vol.sh down } |
||||
|
||||
"Lock Screen" { spawn sxmo_screenlock; wait } |
||||
"Windowify" { spawn sxmo_phonecallerwindowify.tcl; wait } |
||||
|
||||
"Hangup" { hangup $modem_pid } |
||||
} |
||||
} |
||||
} |
||||
|
||||
proc hangup {modem_pid} { |
||||
spawn sxmo_megiaudioroute -h |
||||
send -i $modem_pid "ATH;\r" |
||||
expect { |
||||
-i $modem_pid "OK" exit |
||||
} |
||||
puts "Failed to hangup?" |
||||
exit |
||||
} |
||||
|
||||
proc dial {modem_pid} { |
||||
puts "Dialer" |
||||
set number [dialermenu] |
||||
call $modem_pid $number |
||||
} |
||||
|
||||
proc pickup {modem_pid number} { |
||||
spawn rm -f /tmp/sxmo_incomingcall |
||||
puts "Pickup" |
||||
send -i $modem_pid "ATA;\r" |
||||
expect { |
||||
-i $modem_pid "OK" { incallmenu $modem_pid $number } |
||||
} |
||||
puts "Failed to pickup?" |
||||
exit |
||||
} |
||||
|
||||
switch [lindex $argv 0] { |
||||
"pickup" {pickup [setup] [lindex $argv 1]} |
||||
"dial" {dial [setup]} |
||||
} |
@ -0,0 +1,10 @@ |
||||
#!/usr/bin/env tclsh |
||||
package require Tk |
||||
|
||||
proc callwindowify {number} { |
||||
font create AppHighlightFont -size 40 -weight bold |
||||
grid [ttk::label .l -text "In Call with $number" -font AppHighlightFont] |
||||
grid [ttk::button .button -text "Return to Call Menu (unwindowify)" -command exit] |
||||
} |
||||
|
||||
callwindowify [lindex $argv 0] |
@ -0,0 +1,107 @@ |
||||
#!/usr/bin/expect -f |
||||
|
||||
proc setup {} { |
||||
set timeout 1 |
||||
exp_internal 0 |
||||
log_user 0 |
||||
set ::env(DISPLAY) :0 |
||||
spawn sh -c "screen -wipe" |
||||
spawn sh -c "screen -rx dialer || screen -S dialer /dev/ttyUSB2 115200" |
||||
sleep 0.3 |
||||
set modem_pid $spawn_id |
||||
sleep 0.2 |
||||
|
||||
# Set audio routing |
||||
messagemodem $modem_pid "AT+QDAI=1,0,0,2,0,1,1,1;\r" 1 |
||||
# Setup text message to immediatly show text response on +CMT recvs |
||||
messagemodem $modem_pid "AT+CNMI=1,2,0,1,0;\r" 1 |
||||
# Set message format as PDU |
||||
messagemodem $modem_pid "AT+CMGF=0;\r" 1 |
||||
|
||||
return $modem_pid |
||||
} |
||||
|
||||
proc messagemodem {modem_pid message fatal} { |
||||
send -i $modem_pid $message |
||||
expect { |
||||
-i $modem_pid "OK" { |
||||
return 1 |
||||
} |
||||
} |
||||
if {$fatal} { |
||||
puts "Couldn't run command on modem: $message" |
||||
exit |
||||
} |
||||
return 0 |
||||
} |
||||
|
||||
set modem_pid [setup] |
||||
sleep 0.2 |
||||
|
||||
# E.g. consume the thing |
||||
expect -i $modem_pid eof |
||||
|
||||
proc writenumber {number} { |
||||
exec sh -c "echo '$number' > /tmp/sxmo_incomingcall" |
||||
puts "Ring for incoming call: <$number>" |
||||
spawn sxmo_setpineled blue 0 |
||||
sleep 0.3 |
||||
} |
||||
|
||||
proc ring {modem_pid} { |
||||
send -i $modem_pid "AT+CLCC\r" |
||||
expect { |
||||
-i $modem_pid -re {\n\+CLCC: 4,.+\+(\d+)} { writenumber $expect_out(1,string) } |
||||
-i $modem_pid -re {\n\+CLCC: 3} { writenumber "UNKNOWN" } |
||||
} |
||||
} |
||||
|
||||
proc textmsg {modem_pid pdutext} { |
||||
puts "New text msg - pdu text $pdutext" |
||||
spawn sxmo_pdudecode $pdutext |
||||
wait |
||||
expect -i $spawn_id -re {Date: ([\-0-9]+)\r\nNumber: \+?([^\n]+)\r\nMessage:\r\n(.+)$} { puts "Matched ok"; } |
||||
set date $expect_out(1,string) |
||||
set number $expect_out(2,string) |
||||
set msg [string trim $expect_out(3,string)] |
||||
|
||||
puts "<date: $date>" |
||||
puts "<number: $number>" |
||||
puts "<msg: $msg>" |
||||
|
||||
spawn date "+%Y/%m/%d %H:%M:%S" -d "@$date" |
||||
wait |
||||
expect |
||||
set prettydate [string trim $expect_out(buffer)] |
||||
|
||||
set USER $::env(USER) |
||||
spawn mkdir -p "/home/$USER/.sxmo/$number" |
||||
wait |
||||
set fp "/home/$USER/.sxmo/$number/log.txt" |
||||
|
||||
set logf [open $fp a] |
||||
puts $logf "$number at \[$prettydate\]:\n$msg\n" |
||||
close $logf |
||||
} |
||||
|
||||
proc nocarrier {modem_pid} { |
||||
spawn sxmo_setpineled blue 0 |
||||
exec sh -c "rm -f /tmp/sxmo_incomingcall" |
||||
puts "No carrier" |
||||
} |
||||
|
||||
while 1 { |
||||
set is_ringing 0 |
||||
set timeout 2 |
||||
expect { |
||||
-i $modem_pid -re {\nRING\r} {ring $modem_pid; set is_ringing 1} |
||||
-i $modem_pid -re {\nNO CARRIER\r} {nocarrier $modem_pid; set is_ringing 0} |
||||
-i $modem_pid -re {\n\+CMT: \S+\r\n([0-9A-F]+)} {textmsg $modem_pid $expect_out(1,string)} } |
||||
if { $is_ringing } { |
||||
spawn sxmo_setpineled blue 1 |
||||
} else { |
||||
spawn sxmo_setpineled blue 0 |
||||
} |
||||
|
||||
sleep 2 |
||||
} |
@ -0,0 +1,8 @@ |
||||
#!/usr/bin/env sh |
||||
DIR=/home/$USER/.sxmo |
||||
|
||||
# Warn for no texts |
||||
ls -1 $DIR | wc -l | grep -E '^0$' && echo "No texts!" | dmenu -fn Terminus-20 -l 10 -c && exit 1 |
||||
|
||||
# Display |
||||
ls -1 $DIR | dmenu -p Messages -c -fn Terminus-20 -l 10 | xargs -INUMBER st -e tail -f $DIR/NUMBER/sms.txt |
@ -1,15 +0,0 @@ |
||||
move() { |
||||
xdotool keydown Alt |
||||
echo "Move done?" | dmenu |
||||
xdotool keyup Alt |
||||
} |
||||
|
||||
resize() { |
||||
xdotool keydown Alt |
||||
xdotool mousedown 3 |
||||
echo "Resize done?" | dmenu |
||||
xdotool keyup Alt |
||||
xdotool mouseup 3 |
||||
} |
||||
|
||||
$@ |
@ -1,82 +0,0 @@ |
||||
#!/usr/bin/expect -f |
||||
|
||||
proc setup {} { |
||||
set timeout 5 |
||||
exp_internal 1 |
||||
# Cleanup - set DISPLAY & if screen to modem already on, kill it |
||||
set ::env(DISPLAY) :0 |
||||
#spawn screen -XS dialer quit |
||||
spawn sh -c "screen -rx dialer || screen -S dialer /dev/ttyUSB2 115200" |
||||
return $spawn_id |
||||
} |
||||
|
||||
proc dialermenu {} { |
||||
# Prompt user for number to dial |
||||
spawn sh -c "pidof svkbd-sxmo || svkbd-sxmo" |
||||
spawn sh -c " |
||||
echo Test Number 804-222-1111 | |
||||
dmenu -l 10 -p Number -c -fn Terminus-20 | |
||||
awk -F' ' '{print \$NF}' | |
||||
tr -d - |
||||
" |
||||
wait |
||||
expect -re "(\\d+)" |
||||
return $expect_out(buffer) |
||||
} |
||||
|
||||
proc call {modem_pid number} { |
||||
puts "Calling <$number>" |
||||
send -i $modem_pid "ATD$number;\r" |
||||
expect { |
||||
-i $modem_pid "OK" { incallmenu $modem_pid $number } |
||||
} |
||||
spawn sh -c "echo 'Failed to connect?' | dmenu -fn Terminus-20 -c" |
||||
wait |
||||
exit |
||||
} |
||||
|
||||
proc incallmenu {modem_pid number} { |
||||
while 1 { |
||||
spawn sh -c " |
||||
echo -e ' |
||||
Mute |
||||
Hangup |
||||
Lock Screen |
||||
Number Input |
||||
' | sed -r '/^\s*$/d' | awk '{\$1=\$1};1' | dmenu -c -fn Terminus-20 -l 10 -p '$number' |
||||
" |
||||
wait |
||||
expect { |
||||
"Mute" mute |
||||
"Volume ↑" { spawn sxmo_vol.sh up } |
||||
"Volume ↓" { spawn sxmo_vol.sh down } |
||||
"Numberpad" { spawn sxmo_vol.sh down } |
||||
"Unmute" unmute |
||||
"Hangup" { hangup $modem_pid } |
||||
} |
||||
} |
||||
} |
||||
|
||||
proc hangup {modem_pid} { |
||||
send -i $modem_pid "ATH;\r" |
||||
expect { |
||||
-i $modem_pid "OK" exit |
||||
} |
||||
puts "Failed to hangup?" |
||||
exit |
||||
} |
||||
|
||||
proc dial {modem_pid} { |
||||
puts "Dialer" |
||||
set number [dialermenu] |
||||
call $modem_pid $number |
||||
} |
||||
|
||||
proc pickup {modem_pid} { |
||||
puts "Pickup" |
||||
} |
||||
|
||||
switch [lindex $argv 0] { |
||||
"pickup" {pickup [setup]} |
||||
"dial" {dial [setup]} |
||||
} |
@ -1,34 +0,0 @@ |
||||
#!/usr/bin/expect -f |
||||
|
||||
proc setup {} { |
||||
set timeout 5 |
||||
#exp_internal 1 |
||||
log_user 0 |
||||
set ::env(DISPLAY) :0 |
||||
spawn sh -c "screen -wipe" |
||||
spawn sh -c "screen -rx dialer || screen -S dialer /dev/ttyUSB2 115200" |
||||
return $spawn_id |
||||
} |
||||
|
||||
set modem_pid [setup] |
||||
sleep 0.2 |
||||
|
||||
send -i $modem_pid "AT+CNUM\r" |
||||
expect -i $modem_pid -re {\n\+CNUM: ([^\r]+)\r} |
||||
set f $expect_out(1,string) |
||||
puts "My number: <$f>" |
||||
|
||||
send -i $modem_pid "AT+CSQ\r" |
||||
expect -i $modem_pid -re {\n\+CSQ: ([^\r]+)\r} |
||||
set f $expect_out(1,string) |
||||
puts "Modem signal strength: <$f>" |
||||
|
||||
send -i $modem_pid "AT+QCCID\r" |
||||
expect -i $modem_pid -re {\n\+QCCID: ([^\r]+)\r} |
||||
set f $expect_out(1,string) |
||||
puts "ICCID: <$f>" |
||||
|
||||
send -i $modem_pid "AT+QNWINFO\r" |
||||
expect -i $modem_pid -re {\n\+QNWINFO: ([^\r]+)\r} |
||||
set f $expect_out(1,string) |
||||
puts "Network Info: <$f>" |
@ -1,45 +0,0 @@ |
||||
#!/usr/bin/expect -f |
||||
|
||||
proc setup {} { |
||||
set timeout 1 |
||||
exp_internal 0 |
||||
log_user 0 |
||||
set ::env(DISPLAY) :0 |
||||
spawn sh -c "screen -wipe" |
||||
spawn sh -c "screen -rx dialer || screen -S dialer /dev/ttyUSB2 115200" |
||||
#spawn dialerscreen.sh |
||||
#spawn sh -c "screen -S dialer /dev/ttyUSB2 115200" |
||||
return $spawn_id |
||||
} |
||||
|
||||
set modem_pid [setup] |
||||
sleep 0.2 |
||||
|
||||
# E.g. consume the thing |
||||
expect -i $modem_pid eof |
||||
|
||||
proc ring {modem_pid} { |
||||
puts "Ringing" |
||||
|
||||
send -i $modem_pid "AT+CLCC\r" |
||||
expect -i $modem_pid -re {\n\+CLCC: 3,([^\r]+)\r} |
||||
set f $expect_out(1,string) |
||||
puts "Modem signal strength: <$f>" |
||||
|
||||
} |
||||
proc nocarrier {modem_pid} { |
||||
puts "No carrier" |
||||
} |
||||
|
||||
while 1 { |
||||
set is_ringing 0 |
||||
set timeout 2 |
||||
#send -i $modem_pid "AT+CPAS\r" |
||||
expect { |
||||
-i $modem_pid -re {\nRING\r} {ring $modem_pid} |
||||
-i $modem_pid -re {\nNO CARRIER\r} {nocarrier $modem_pid} |
||||
} |
||||
#set status $expect_out(1,string) |
||||
#puts "My number: <$status>" |
||||
sleep 2 |
||||
} |
Loading…
Reference in new issue