Skip to content

Commit

Permalink
Add subgroup selection in web UI
Browse files Browse the repository at this point in the history
When obtaining BASE info, create a list of (radio button)
selectors for each subgroup.

Pressing the source card outside the selectors will add the source
with the selected BIS indexes (found in subgroup)
  • Loading branch information
larsgk committed Aug 29, 2024
1 parent d4499db commit 74c34e3
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 40 deletions.
17 changes: 16 additions & 1 deletion web/components/source-device-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class SourceDeviceList extends HTMLElement {

this.sourceFound = this.sourceFound.bind(this);
this.sourceUpdated = this.sourceUpdated.bind(this);
this.baseUpdated = this.baseUpdated.bind(this);
this.sourceClicked = this.sourceClicked.bind(this);

const shadowRoot = this.attachShadow({mode: 'open'});
Expand All @@ -79,7 +80,7 @@ export class SourceDeviceList extends HTMLElement {

this.#model.addEventListener('source-found', this.sourceFound);
this.#model.addEventListener('source-updated', this.sourceUpdated);
this.#model.addEventListener('base-updated', this.sourceUpdated);
this.#model.addEventListener('base-updated', this.baseUpdated);
this.#model.addEventListener('reset', () => { this.#list.replaceChildren()})
}

Expand Down Expand Up @@ -174,6 +175,20 @@ export class SourceDeviceList extends HTMLElement {
console.warn('source not found!', source);
}
}

baseUpdated(evt) {
const { source } = evt.detail;

const items = Array.from(this.#list.querySelectorAll('source-item'));

const el = items.find(i => i.getModel() === source);

if (el) {
el.baseUpdated();
} else {
console.warn('source not found!', source);
}
}
}


Expand Down
131 changes: 92 additions & 39 deletions web/components/source-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ import { BT_DataType, bufToAddressString } from '../lib/message.js';
const template = document.createElement('template');
template.innerHTML = `
<style>
/* Styles go here */
div {
display: block;
#card {
display: grid;
grid-template-columns: 1fr 1fr;
row-gap: 5px;
position: relative;
box-sizing: border-box;
min-width: 5.14em;
height: 100px;
min-height: 100px;
margin: 0.2em;
background: transparent;
text-align: center;
Expand All @@ -35,58 +36,73 @@ div {
}
#name {
position: absolute;
left: 5px;
top: 5px;
font-size: 1.2em;
text-align: left;
}
#addr {
position: absolute;
left: 5px;
top: 30px;
grid-column: 1 / 3;
font-size: 0.9em;
text-align: left;
}
#base {
grid-column: 1 / 3;
font-size: 0.9em;
text-align: left;
}
#broadcast_name {
position: absolute;
top: 5px;
font-size: 1.2em;
text-align: right;
}
#broadcast_id {
position: absolute;
left: 5px;
bottom: 5px;
font-size: 0.9em;
text-align: left;
}
#rssi {
position: absolute;
right: 5px;
bottom: 5px;
font-size: 0.9em;
}
#base {
position: absolute;
left: 5px;
bottom: 25px;
font-size: 0.9em;
text-align: right;
}
#card[state="selected"] {
background-color: lightgreen;
box-shadow: 1px 1px 2px 2px gray;
}
.subgroup {
border: 1px solid blue;
border-radius: 5px;
box-sizing: border-box;
padding: 8px;
color: blue;
text-align: left;
}
.subgroup.selected {
background: blue;
color: white
}
#subgroups {
display: flex;
flex-direction: column;
grid-column: 1 / 3;
font-size: 0.9em;
row-gap: 5px;
}
</style>
<div id="card">
<span id="name"></span>
<span id="broadcast_name"></span>
<span id="base">BASE: Pending...</span>
<span id="addr"></span>
<span id="broadcast_id"></span>
<span id="rssi"></span>
<span id="base">BASE:</span>
<div id="subgroups"></div>
</div>
`;

Expand Down Expand Up @@ -115,6 +131,7 @@ export class SourceItem extends HTMLElement {
#broadcastIdEl
#rssiEl
#baseEl
#subgroupsEl

constructor() {
super();
Expand All @@ -134,6 +151,7 @@ export class SourceItem extends HTMLElement {
this.#broadcastIdEl = this.shadowRoot?.querySelector('#broadcast_id');
this.#rssiEl = this.shadowRoot?.querySelector('#rssi');
this.#baseEl = this.shadowRoot?.querySelector('#base');
this.#subgroupsEl = this.shadowRoot?.querySelector('#subgroups');
}

baseInfoString(base) {
Expand All @@ -144,25 +162,55 @@ export class SourceItem extends HTMLElement {
} else {
// Subgroups
let sg_count = 0;
base.subgroups?.forEach(subgroup => {
let sg_str = `SG[${sg_count}]:(`;
result += ` ${base.subgroups?.length || 0} subgroup(s) found`;
}

return result;
}

refreshSubgroups(base) {
this.#subgroupsEl.innerHTML = '';

const handleSelectSubgroup = (evt) => {
const el = evt.target;
base.subgroups?.forEach((subgroup, idx) => {
subgroup.isSelected = idx === el.subgroupIdx ? true : false;
});
// 'Select' item in UI
this.#subgroupsEl.querySelectorAll('.subgroup').forEach(sgEl => {
sgEl.classList.toggle('selected', el.subgroupIdx === sgEl.subgroupIdx);
});
evt.stopPropagation();
}

if (!base) {
return;
} else {
base.subgroups?.forEach((subgroup, idx) => {
subgroup.isSelected = idx === 0 ? true : false;
// find/grab relevant meta & codec cfg info
const item = document.createElement('div');
item.addEventListener('click', handleSelectSubgroup);
item.subgroupIdx = idx;
item.classList.add('subgroup');
item.classList.toggle('selected', subgroup.isSelected);

let sg_str = `Subgroup #${idx} (w/${subgroup.bises?.length || 0} BIS):`;
const str_tk = [];
const freq = subgroup.codec_data?.find(i => i.name === "SamplingFrequency")?.value;
if (freq) {
str_tk.push(`Freq: ${freq}Hz`);
str_tk.push(`${freq/1000}KHz`);
}
const lang = subgroup.codec_meta?.find(i => i.name === "Language")?.value;
if (lang) {
str_tk.push(`Lang: ${lang}`);
}
str_tk.push(`BIS_CNT=${subgroup.bises?.length || 0}`)
sg_str += str_tk.join(', ');
sg_str += ")";

console.log(sg_str);
result += sg_str;

sg_count++;
})
item.textContent = sg_str;
this.#subgroupsEl.appendChild(item);
});
}

return result;
}

refresh() {
Expand All @@ -171,13 +219,18 @@ export class SourceItem extends HTMLElement {
this.#broadcastNameEl.textContent = this.#source.broadcast_name;
this.#addrEl.textContent = `Addr: ${addrString(this.#source.addr)}`;
this.#rssiEl.textContent = `RSSI: ${this.#source.rssi}`;
this.#baseEl.textContent = this.baseInfoString(this.#source.base);
this.#broadcastIdEl.textContent = `Broadcast ID: 0x${
this.#source.broadcast_id?.toString(16).padStart(6, '0').toUpperCase()}`;

this.#cardEl.setAttribute('state', this.#source.state);
}

baseUpdated() {
this.#baseEl.textContent = this.baseInfoString(this.#source.base);

this.refreshSubgroups(this.#source.base);
}

setModel(source) {
this.#source = source;

Expand Down
6 changes: 6 additions & 0 deletions web/lib/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,12 @@ export const tvArrayToLtv = arr => {
case BT_DataType.BT_DATA_BROADCAST_CODE:
outArr = Array.from(value); //uint8 array
break;
case BT_DataType.BT_DATA_BIS_SYNC:
outArr = [];
value.forEach(v => {
outArr.push(...uintToArray(v,4));
});
break;
default:
// Don't add fields we don't handle yet
continue;
Expand Down
25 changes: 25 additions & 0 deletions web/models/assistant-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,31 @@ class AssistantModel extends EventTarget {
addr,
];

// If the source has BASE information and there is more than one subgroup,
// send 0's for all subgroups except the last, which will be 0xFFFFFFFF (no pref)
if (source.base?.subgroups?.length > 1) {
const value = [];

source.base?.subgroups?.forEach(sg => {
if (sg.isSelected) {
// For each BIS in Subgroup, set bit corresponding to index
let bis_sync = 0;
sg.bises?.forEach(bis => {
if (bis.index) {
bis_sync += 1 << (bis.index-1);
}
})
value.push(bis_sync);
} else {
value.push(0);
}
});

tvArr.push({ type: BT_DataType.BT_DATA_BIS_SYNC, value });

console.log("BIS SYNC TO", value);
}

const payload = tvArrayToLtv(tvArr);

console.log('Add Source payload', payload)
Expand Down

0 comments on commit 74c34e3

Please sign in to comment.