Looking for Fusion Wallboard

Status
Not open for further replies.
I've demonstrated it to many people and there are several people using it. If you want a demo then please call the number I provided.
 
Last edited:
@DigitalDaz Looking good!

I had to code my own for a customer, simply because I just could not make the financials work for Purple membership.

I produced a version of a wall board that does not put any extra load on Freeswitch for each instance of the wall board. I achieve this by setting up a hook script for call center events, that writes to an in memory filing system, this way I have no need to connect to the FS event socket. The wall board(s) then get their updates using Server Side Events (SSE) via a php script that reads from the in memory file system so the only load is on the web server.

I offered to share my work with the Fusion Team, but there was no interest.
 
  • Like
Reactions: afshin
@DigitalDaz Looking good!

I had to code my own for a customer, simply because I just could not make the financials work for Purple membership.

I produced a version of a wall board that does not put any extra load on Freeswitch for each instance of the wall board. I achieve this by setting up a hook script for call center events, that writes to an in memory filing system, this way I have no need to connect to the FS event socket. The wall board(s) then get their updates using Server Side Events (SSE) via a php script that reads from the in memory file system so the only load is on the web server.

I offered to share my work with the Fusion Team, but there was no interest.

Share it here!!!

I'm using redis for a couple of the counters so similar.
 
I am happy to share anything I have done. BUT The last thing I want to do is upset Mark. Sadly this is an area where the membership/closed source thing can become divisive.

Ok the hook script line in /etc/freeswitch/autoload_configs/lua.conf.xml:
Code:
    <!-- AHF for Wallboard -->
    <hook event="CUSTOM" subclass="callcenter::info" script="a2es_cc_events.lua"/>

The file a2es_cc_events.lua is fairly streamline, my intention was for this script not to do any more work that absolutely necessary, so for example, it does not create directories if they don't exist, all directories etc. are created by a separate maintenance script. There's also some stuff in there commented out that I didn't need.

a2es_cc_events.lua:
Code:
run_dir = "/var/run/a2es_cc_events";
--os.execute("mkdir -p " .. run_dir);

cc_action = event:getHeader("CC-Action");
write_event_flag = false;

if cc_action == "agent-status-change" then
    write_event_flag = true;
    cc_agent = event:getHeader("CC-Agent");
    cc_agent_status = event:getHeader("CC-Agent-Status");
    out_file = run_dir .. "/a-" .. cc_agent .. "/logs/status.log";
    local file = assert(io.open(out_file, "a"));
        file:write(cc_agent_status .. ": " .. os.time() .. "\n");
        file:close();
    out_file = run_dir .. "/a-" .. cc_agent .. "/" .. cc_action .. "/data";

elseif cc_action == "agent-state-change" then
    write_event_flag = true;
    cc_agent = event:getHeader("CC-Agent");
    out_file = run_dir .. "/a-" .. cc_agent .. "/" .. cc_action .. "/data";

elseif cc_action == "agent-offering" then
    cc_queue = event:getHeader("CC-Queue");
    out_file = run_dir .. "/q-" .. cc_queue .. "/" .. cc_action .. "/data";

elseif cc_action == "bridge-agent-start" then
    cc_queue = event:getHeader("CC-Queue");
    out_file = run_dir .. "/q-" .. cc_queue .. "/counters/answered.count";
    local file = assert(io.open(out_file, "a"));
        file:write("\n");
        file:close();
    out_file = run_dir .. "/q-" .. cc_queue .. "/" .. cc_action .. "/data";

--elseif cc_action == "bridge-agent-end" then
--    cc_queue = event:getHeader("CC-Queue");
--    out_file = run_dir .. "/q-" .. cc_queue .. "/" .. cc_action .. "/data";

--elseif cc_action == "bridge-agent-fail" then
--    cc_queue = event:getHeader("CC-Queue");
--    out_file = run_dir .. "/q-" .. cc_queue .. "/" .. cc_action .. "/data";

--elseif cc_action == "member-queue-start" then
--    cc_queue = event:getHeader("CC-Queue");
--    out_file = run_dir .. "/q-" .. cc_queue .. "/" .. cc_action .. "/data";

elseif cc_action == "member-queue-end" then
    cc_queue = event:getHeader("CC-Queue");
    cc_cause = event:getHeader("CC-Cause");
    cc_cancel_reason = event:getHeader("CC-Cancel-Reason");
    out_file = run_dir .. "/q-" .. cc_queue .. "/counters/" .. cc_cause .. ".count";
    local file = assert(io.open(out_file, "a"));
        file:write("\n");
        file:close();
    out_file = run_dir .. "/q-" .. cc_queue .. "/counters/" .. cc_cancel_reason .. ".count";
    local file = assert(io.open(out_file, "a"));
        file:write("\n");
        file:close();

    out_file = run_dir .. "/q-" .. cc_queue .. "/" .. cc_action .. "/data";

elseif cc_action == "members-count" then
    write_event_flag = true;
    cc_queue = event:getHeader("CC-Queue");
    out_file = run_dir .. "/q-" .. cc_queue .. "/" .. cc_action .. "/data";

else
    out_file = run_dir .. "/unknown/data";

end

if write_event_flag then
    local file = assert(io.open(out_file .. ".plain", "w"));
        file:write(event:serialize());
        file:close();

    --local file = assert(io.open(out_file .. ".json", "w"));
        --file:write(event:serialize("json"));
        --file:close();
end
 
Just thought some explanation of the directory structure may be helpful, below is an example structure of a callcenter queue with seven agents, I have removed five of them to make it easier to read. The agents data is held in the directories prefixed with an 'a', the queue data is in the directory prefixed with a 'q' and authorised IPs and Queue information is in the directory prefixed with a 'd'

The data.plain files are simply the last event of that type captured. status.log contains a sequential list of agent state changes with timestamps. The files with the suffix .count are crude counters, they are incremented by adding a newline to the file, thus the file size (or it's line count) is its count:

Code:
.
├── a-2e94b96d-301f-428c-97d4-c3ee1ae1498e
│   ├── agent-state-change
│   │   └── data.plain
│   ├── agent-status-change
│   │   └── data.plain
│   └── logs
│       └── status.log
├── a-309f8c41-b9ee-4972-ae74-ec58650e3a40
│   ├── agent-state-change
│   │   └── data.plain
│   ├── agent-status-change
│   │   └── data.plain
│   └── logs
│       └── status.log
├── d-e930a2d6-e55e-4102-843e-3f2a468d0958
│   ├── authip.plain
│   └── queues.plain
└── q-7c423ed4-ca36-4781-9161-c0bda55b392d
    ├── agent-offering
    ├── bridge-agent-end
    ├── bridge-agent-fail
    ├── bridge-agent-start
    ├── counters
    │   ├── answered.count
    │   ├── BREAK_OUT.count
    │   ├── Cancel.count
    │   ├── NO_AGENT_TIMEOUT.count
    │   ├── NONE.count
    │   ├── Terminated.count
    │   └── TIMEOUT.count
    ├── member-queue-end
    ├── member-queue-start
    ├── members-count
    │   └── data.plain
    └── static
        ├── agents.plain
        ├── authip.plain
        └── data.plain

The resulting wallboard screen looks like this, as you can see, most of the agents on my test box have not logged in for a very long time!

Screenshot from 2020-11-27 18-11-20.png
 
  • Like
Reactions: vin3 and afshin
Thanks Adrian, very useful. I'll have a good read later and get my head around whats going on. I like redis so I'd like to keep my data in there, I also like my initial experiments with websockets so may mess with some kind of pub/sub system. Its about time I took an interest in these technologies.
 
  • Like
Reactions: Adrian Fretwell
redis, may be a better option or me, I only went the way I did to keep the load on FreeSWITCH to a minimum. Please keep me in the loop, I'm pretty sure you have acces to my personal email address.
 
  • Like
Reactions: DigitalDaz
@DigitalDaz Did you get redis working with FreeSWITCH lua? If so, would you share the details please. I ask because I am just about to start migrating my in-memory filing system to redis for the wallboard and didn't want to re-invent the wheel.
 
Have a customer looking for wallboard. Wonder if you guys make any progress and would you be interested to share some of the work (paying for not a problem) but jumping to Purple monthly charge for a single client doesnt make too much sense.
 
Have a customer looking for wallboard. Wonder if you guys make any progress and would you be interested to share some of the work (paying for not a problem) but jumping to Purple monthly charge for a single client doesnt make too much sense.
just my two cents but it comes with a lot more than just a wallboard lol and that wallboard you do get will be sure to work with all update. I really hated the idea of becoming purple but after a couple months it has been a life saver and a huge load off my should knowing that everything on my servers is done by design from the designer.
 
These is nothing difficult in understanding what FusionPBX does. If you are competent with php, lua and the linux command line, then you can build your own anything to your own specifications including a wallboard. Again, if you are competent, upgrading your code to work with future releases should not be an issue.
 
Here's an interesting Freeswitch based wallboard: https://github.com/khaefner/queue-view

I've installed it and can confirm it works however it's not multi-tenant and the agent name shows up as the UUID and not the name. So would need a little work to convert it for FusionPBX but it's a great start and is quite a nice little wallboard.

The two other options are Mark's wallboard which is a bit pricey if you don't have that many clients or you can also use Queuemetrics which is one on of the better wallboard and reporting solutions on the market (pay per user minimum 5 users)
 
  • Like
Reactions: lenz
Status
Not open for further replies.