Browse Source

mesh messages

main
gsd 6 months ago
parent
commit
ffc8efc709
  1. 27
      pipboyMESH/mesh_server.py
  2. 5
      pipboyMESH/mesht_models.py
  3. 4
      pipboyUI/dist/pipboy-ui/index.html
  4. 1
      pipboyUI/dist/pipboy-ui/main.448a0ec1991b3365.js
  5. 1
      pipboyUI/dist/pipboy-ui/main.8cbe3e37e9415df2.js
  6. 2
      pipboyUI/dist/pipboy-ui/styles.688d38c9b65f26a9.css
  7. 8
      pipboyUI/src/app/app-routing.module.ts
  8. 12
      pipboyUI/src/app/app.module.ts
  9. 2
      pipboyUI/src/app/components/LowerHeaders/Inventory/inventory-apps.component.ts
  10. 2
      pipboyUI/src/app/components/LowerHeaders/Radio/radio.component.ts
  11. 12
      pipboyUI/src/app/components/LowerHeaders/meshtastic/mesh-channel.component.ts
  12. 64
      pipboyUI/src/app/components/LowerHeaders/meshtastic/mesh-list-node.component.ts
  13. 94
      pipboyUI/src/app/components/LowerHeaders/meshtastic/mesh-messages.component.ts
  14. 21
      pipboyUI/src/app/components/UpperHeaders/meshtastic-header.component.ts
  15. 48
      pipboyUI/src/app/components/abstract/AbsListSelect.ts
  16. 81
      pipboyUI/src/app/services/MeshtasticService.ts
  17. 11
      pipboyUI/src/styles.scss

27
pipboyMESH/mesh_server.py

@ -11,6 +11,7 @@ import sys
import asyncio import asyncio
from time import time from time import time
from typing import List, Dict from typing import List, Dict
import random
#msh imports #msh imports
from transport_serial import SerialTransport from transport_serial import SerialTransport
@ -195,7 +196,9 @@ class Servlet:
@self.app.get(route + "/nodes/{id}") @self.app.get(route + "/nodes/{id}")
async def getNodeInfo(id: int): async def getNodeInfo(id: int):
if id in self.nodes: if id in self.nodes:
return self.nodes[id] #print("found")
#print(self.nodes[id].__dict__)
return self.nodes[id].__dict__
else: else:
raise HTTPException(404) raise HTTPException(404)
@ -210,10 +213,28 @@ class Servlet:
@self.app.post(route + "/messages") @self.app.post(route + "/messages")
#curl -X POST -d '{"txt":"test", "node_id":"xxxxxxxxxx"}' -H "Content-Type: application/json" http://127.0.0.1:8868/mesh/api/messages #curl -X POST -d '{"txt":"test", "node_id":"xxxxxxxxxx"}' -H "Content-Type: application/json" http://127.0.0.1:8868/mesh/api/messages
async def postMessage(msg: NewMessage): async def postMessage(msg: NewMessage):
magicpkg = {
"packet": {
"from": self.device.nid,
"to": PUB_CH,
"decoded": {"payload": msg.txt.encode()},
"id": random.randint(1, 0x7FFFFFFF),
}
}
async def genNewMsg(from_radio):
msg = Message(from_radio)
self.message.append(msg)
await self.ws_update({"type":WS_TYPE_NEW, "event": WS_EVENT_MESSAGE, "data": msg.__dict__})
self.lstChange["messages"] = time()
if msg.node_id: if msg.node_id:
return await self.device.sendMsgToDM(msg.txt, msg.node_id) res = await self.device.sendMsgToDM(msg.txt, msg.node_id)
magicpkg["to"] = msg.node_id
else: else:
return await self.device.sendMsgToChannel(msg.txt, msg.channel_id) magicpkg["channel"] = msg.channel_id
res = await self.device.sendMsgToChannel(msg.txt, msg.channel_id)
await genNewMsg(magicpkg)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()

5
pipboyMESH/mesht_models.py

@ -46,7 +46,10 @@ class Channel:
class Node: #aka node info class Node: #aka node info
def __init__(self, fr): def __init__(self, fr):
self.num = fr["node_info"]["num"] self.num = fr["node_info"]["num"]
self.user = fr["node_info"].get("user", {}) self.user = {}
self.user["id"] = fr["node_info"].get("user", {}).get("id", None)
self.user["short_name"] = fr["node_info"].get("user", {}).get("short_name", None)
self.user["long_name"] = fr["node_info"].get("user", {}).get("long_name", None)
self.snr = fr["node_info"].get("snr", None) self.snr = fr["node_info"].get("snr", None)
self.last_heard = fr["node_info"].get("last_heard", None) self.last_heard = fr["node_info"].get("last_heard", None)
self.hops_away = fr["node_info"].get("hops_away", None) self.hops_away = fr["node_info"].get("hops_away", None)

4
pipboyUI/dist/pipboy-ui/index.html

File diff suppressed because one or more lines are too long

1
pipboyUI/dist/pipboy-ui/main.448a0ec1991b3365.js

File diff suppressed because one or more lines are too long

1
pipboyUI/dist/pipboy-ui/main.8cbe3e37e9415df2.js

File diff suppressed because one or more lines are too long

2
pipboyUI/dist/pipboy-ui/styles.6561013e58b05f16.css → pipboyUI/dist/pipboy-ui/styles.688d38c9b65f26a9.css

File diff suppressed because one or more lines are too long

8
pipboyUI/src/app/app-routing.module.ts

@ -13,7 +13,7 @@ import {InventoryCustomComponent} from "./components/LowerHeaders/Inventory/inve
import {MeshtasticHeaderComponent} from "./components/UpperHeaders/meshtastic-header.component"; import {MeshtasticHeaderComponent} from "./components/UpperHeaders/meshtastic-header.component";
import {MeshMyNodeComponent} from "./components/LowerHeaders/meshtastic/mesh-my-node.component"; import {MeshMyNodeComponent} from "./components/LowerHeaders/meshtastic/mesh-my-node.component";
import {MeshListNodeComponent} from "./components/LowerHeaders/meshtastic/mesh-list-node.component"; import {MeshListNodeComponent} from "./components/LowerHeaders/meshtastic/mesh-list-node.component";
import {MeshChannelComponent} from "./components/LowerHeaders/meshtastic/mesh-channel.component"; import {MeshMessagesComponent} from "./components/LowerHeaders/meshtastic/mesh-messages.component";
const routes: Routes = [ const routes: Routes = [
{ {
@ -58,9 +58,9 @@ const routes: Routes = [
component: MeshtasticHeaderComponent, component: MeshtasticHeaderComponent,
children: [ children: [
{path: '', redirectTo: 'my', pathMatch: 'full'}, {path: '', redirectTo: 'my', pathMatch: 'full'},
{path: "my", component: MeshMyNodeComponent}, {path: "my", component: MeshMyNodeComponent },
{path: "list", component: MeshListNodeComponent}, {path: "list", component: MeshListNodeComponent },
{path: "channel/:index", component: MeshChannelComponent} {path: "messages/:index", component: MeshMessagesComponent }
] ]
} }
] ]

12
pipboyUI/src/app/app.module.ts

@ -25,7 +25,10 @@ import {KeyboardAppComponent} from "./components/apps/keyboard-app.component";
import {MeshtasticHeaderComponent} from "./components/UpperHeaders/meshtastic-header.component"; import {MeshtasticHeaderComponent} from "./components/UpperHeaders/meshtastic-header.component";
import {MeshMyNodeComponent} from "./components/LowerHeaders/meshtastic/mesh-my-node.component"; import {MeshMyNodeComponent} from "./components/LowerHeaders/meshtastic/mesh-my-node.component";
import {MeshListNodeComponent} from "./components/LowerHeaders/meshtastic/mesh-list-node.component"; import {MeshListNodeComponent} from "./components/LowerHeaders/meshtastic/mesh-list-node.component";
import {MeshChannelComponent} from "./components/LowerHeaders/meshtastic/mesh-channel.component"; import {MatProgressSpinnerModule} from "@angular/material/progress-spinner";
import {MeshMessagesComponent} from "./components/LowerHeaders/meshtastic/mesh-messages.component";
import {HttpClientModule} from "@angular/common/http";
import {MatSnackBarModule} from "@angular/material/snack-bar";
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -47,17 +50,20 @@ import {MeshChannelComponent} from "./components/LowerHeaders/meshtastic/mesh-ch
MeshtasticHeaderComponent, MeshtasticHeaderComponent,
MeshMyNodeComponent, MeshMyNodeComponent,
MeshListNodeComponent, MeshListNodeComponent,
MeshChannelComponent MeshMessagesComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
HttpClientModule,
AppRoutingModule, AppRoutingModule,
BrowserAnimationsModule, BrowserAnimationsModule,
MatDialogModule, MatDialogModule,
MatInputModule, MatInputModule,
FormsModule, FormsModule,
MatButtonModule, MatButtonModule,
MatIconModule MatIconModule,
MatProgressSpinnerModule,
MatSnackBarModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

2
pipboyUI/src/app/components/LowerHeaders/Inventory/inventory-apps.component.ts

@ -39,7 +39,7 @@ export interface InvApp {
</div> </div>
` `
}) })
export class InventoryAppsComponent extends AbsListSelect { export class InventoryAppsComponent extends AbsListSelect<InvApp, any> {
constructor(protected dialog: MatDialog, constructor(protected dialog: MatDialog,
protected io: IOService, protected io: IOService,
protected apps: AppsService) { protected apps: AppsService) {

2
pipboyUI/src/app/components/LowerHeaders/Radio/radio.component.ts

@ -27,7 +27,7 @@ export interface RadioControl {action: Function, name: string}
</div> </div>
` `
}) })
export class RadioComponent extends AbsListSelect { export class RadioComponent extends AbsListSelect<RadioElement, RadioControl> {
constructor(protected player: PlayerService, constructor(protected player: PlayerService,
protected io: IOService) { protected io: IOService) {
super(); super();

12
pipboyUI/src/app/components/LowerHeaders/meshtastic/mesh-channel.component.ts

@ -1,12 +0,0 @@
import {Component} from "@angular/core";
import {AbsListSelect} from "../../abstract/AbsListSelect";
@Component({
selector: 'app-mesh-channel',
template: `
`
})
export class MeshChannelComponent extends AbsListSelect {
}

64
pipboyUI/src/app/components/LowerHeaders/meshtastic/mesh-list-node.component.ts

@ -1,10 +1,11 @@
import {Component, ElementRef, OnInit, QueryList, ViewChildren} from "@angular/core"; import {Component, ElementRef, OnInit, QueryList, ViewChildren} from "@angular/core";
import {IOService} from "../../../services/IOService"; import {IOService} from "../../../services/IOService";
import {MeshtasticService} from "../../../services/MeshtasticService"; import {MeshNode, MeshNodeShort, MeshtasticService} from "../../../services/MeshtasticService";
import {AbsListSelect} from "../../abstract/AbsListSelect"; import {AbsListSelect} from "../../abstract/AbsListSelect";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
import {AppsService} from "../../../services/AppsService"; import {AppsService} from "../../../services/AppsService";
import {RadioControl} from "../Radio/radio.component"; import {RadioControl} from "../Radio/radio.component";
import {MatSnackBar} from "@angular/material/snack-bar";
@Component({ @Component({
selector: 'app-mesh-list-node', selector: 'app-mesh-list-node',
@ -18,7 +19,12 @@ import {RadioControl} from "../Radio/radio.component";
<div style="width: 2.5%; display: flex; flex-direction: column"></div> <div style="width: 2.5%; display: flex; flex-direction: column"></div>
<div style="width: 45%; display: flex; flex-direction: column" id="info"> <div style="width: 45%; display: flex; flex-direction: column" id="info">
<div style="text-align: center; height: 125px; margin-bottom: 10px" class="secondary-color"> <div style="text-align: center; height: 125px; margin-bottom: 10px" class="secondary-color">
<p>{{selected?.name}}</p> <div *ngIf="!loading" style="display: flex; flex-direction: column">
<p style="margin: 0 0" *ngIf="nodeInfo?.snr">SNR: {{nodeInfo?.snr}} dB</p>
<p style="margin: 0 0" *ngIf="nodeInfo?.hops_away">Hops: {{nodeInfo?.hops_away}}</p>
<p style="margin: 0 0" *ngIf="nodeInfo?.last_heard">Появлялся: {{nodeInfo?.last_heard | date: 'HH:mm dd/MM'}}</p>
</div>
<mat-spinner *ngIf="loading"></mat-spinner>
</div> </div>
<div *ngFor="let el of elementsRight" [class.menu-selected]="el == selectedRight && currentSide == 'RIGHT'"> <div *ngFor="let el of elementsRight" [class.menu-selected]="el == selectedRight && currentSide == 'RIGHT'">
<p style="margin: 0 0">{{el.name}}</p> <p style="margin: 0 0">{{el.name}}</p>
@ -27,22 +33,53 @@ import {RadioControl} from "../Radio/radio.component";
</div> </div>
` `
}) })
export class MeshListNodeComponent extends AbsListSelect implements OnInit { export class MeshListNodeComponent extends AbsListSelect<MeshNodeShort, any> implements OnInit {
constructor(public io: IOService, constructor(public io: IOService,
public meshtastic: MeshtasticService, public meshtastic: MeshtasticService,
protected dialog: MatDialog, protected dialog: MatDialog,
protected apps: AppsService) { protected apps: AppsService,
protected snack: MatSnackBar) {
super(); super();
} }
loading: boolean = false;
nodeInfo?: MeshNode;
override elementsRight:any[] = [ override elementsRight:any[] = [
{name: "Написать", action: () => {this.apps.openKeyboard().subscribe((res) => {console.log(res); this.apps.inApp = false})}}, {name: "Написать", action: () => {
this.apps.openKeyboard().subscribe(
(txt) => {
if (txt) {
this.meshtastic.sendMessage(txt, this.nodeInfo?.num).subscribe(
(res) => {
this.snack.open("Сообщение отправлено", undefined, {duration:2000});
}
)
}
this.apps.inApp = false;
}
)}},
{name: "Сообщения", action: () => {}}, {name: "Сообщения", action: () => {}},
{name: "Трасировка*", action: () => {}} {name: "Трасировка*", action: () => {}}
] ]
override selectedRight:any = this.elementsRight[0] override selectedRight:any = this.elementsRight[0]
/*override isSelected(el: any): boolean {
const res = super.isSelected(el);
if (res && this.selected != undefined) {
this.loading = true;
this.meshtastic.getNodeInfo(this.selected.id).subscribe(
(res: MeshNode) => {
this.nodeInfo = res;
this.loading = false;
}
)
}
return res;
}*/
createSub() { createSub() {
this.sub = this.io.keys.subscribe((code) => { this.sub = this.io.keys.subscribe((code) => {
if (this.apps.inApp) return; if (this.apps.inApp) return;
@ -56,12 +93,26 @@ export class MeshListNodeComponent extends AbsListSelect implements OnInit {
break break
} }
default: { default: {
super.choiceItem(code) if (super.choiceItem(code)) {
this.updateNodeInfo()
}
} }
} }
}); });
} }
updateNodeInfo() {
if (this.selected) {
this.loading = true;
this.meshtastic.getNodeInfo(this.selected.id).subscribe(
(res: MeshNode) => {
this.nodeInfo = res;
this.loading = false;
}
)
}
}
override ngOnInit() { override ngOnInit() {
this.createSub() this.createSub()
this.elements = this.meshtastic.nodes; this.elements = this.meshtastic.nodes;
@ -70,6 +121,7 @@ export class MeshListNodeComponent extends AbsListSelect implements OnInit {
this.elements = this.meshtastic.nodes; this.elements = this.meshtastic.nodes;
if (this.selected == undefined) { if (this.selected == undefined) {
this.selected = this.elements[0]; this.selected = this.elements[0];
this.updateNodeInfo();
} }
} }
) )

94
pipboyUI/src/app/components/LowerHeaders/meshtastic/mesh-messages.component.ts

@ -0,0 +1,94 @@
import {Component, OnInit} from "@angular/core";
import {AbsListSelect} from "../../abstract/AbsListSelect";
import {IOService} from "../../../services/IOService";
import {MeshMessage, MeshtasticService, PUB_CH} from "../../../services/MeshtasticService";
import {AppsService} from "../../../services/AppsService";
import {ActivatedRoute} from "@angular/router";
@Component({
selector: "app-mesh-messages",
template: `
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 84%">
<div style="width: 95%; overflow-y: hidden; height: 200px; scroll-behavior: smooth" id="msgs">
<div class="secondary-color" style="margin-bottom: 5px;" *ngFor="let el of elements" [class.menu-selected]="isSelected(el) && currentSide == 'LEFT'" #menuItem>
<p style="margin: 0 0; font-size: 10px;">
{{meshtastic.getNodeName(el.fr0m)}}
{{meshtastic.getMsgTo(el)}}
{{el.rx_time | date: 'HH:mm:ss dd/MM'}}
[{{el.hop_limit}}]</p>
<p style="margin: 0 0">{{el.msg}}</p>
</div>
</div>
<div style="width: 95%; height: 30px; align-items: center; display: flex; flex-direction: row; justify-content: center">
<div style="margin-left: 2%; margin-right: 2%" *ngFor="let el of elementsRight" [class.menu-selected]="el == selectedRight && currentSide == 'RIGHT'">
<p style="margin: 0 0">{{el.name}}</p>
</div>
</div>
</div>
`
})//todo maybe view any msg shit
export class MeshMessagesComponent extends AbsListSelect<MeshMessage, any> implements OnInit {
constructor(public io: IOService,
public meshtastic: MeshtasticService,
protected apps: AppsService,
protected route: ActivatedRoute) {
super();
}
override elementsRight:any[] = [
{name: "Reply to DM", action: () => {}},
{name: "Reply", action: () => {}},
];
override selectedRight = this.elementsRight[0];
private currentIndex:any;
override ngOnInit() {
this.route.params.subscribe(
(params) => {
this.currentIndex = params["index"];
let upd = () => {};
switch (params["index"]) {
case "-1": {
upd = () => this.elements = this.meshtastic.messages;
break;
}
default: {
upd = () => this.elements = this.meshtastic.messages.filter((msg) => msg.to == PUB_CH && msg.channel == parseInt(params["index"]));
break;
}
}
upd();
this.meshtastic.messagesChange.subscribe((msg) => {
if (msg) {
upd()
}
})
if (this.elements)
this.selected = this.elements[this.elements.length]
}
)
this.createSub();
}
createSub() {
this.sub = this.io.keys.subscribe((code) => {
if (this.apps.inApp) return;
switch (code) {
case "KeyA": {
this.currentSide = "LEFT"
break
}
case "KeyD": {
this.currentSide = "RIGHT";
break
}
default: {
super.choiceItem(code)
}
}
});
}
}

21
pipboyUI/src/app/components/UpperHeaders/meshtastic-header.component.ts

@ -1,7 +1,7 @@
import {Component, OnInit} from "@angular/core"; import {Component, OnInit} from "@angular/core";
import { import {
AVAILABLE, AVAILABLE,
ERR, ERR, MeshMessage,
MeshtasticService, MeshtasticService,
NOT_CONNECTED, NOT_CONNECTED,
RECONNECT, RECONNECT,
@ -12,7 +12,8 @@ import {ActivatedRoute, Router} from "@angular/router";
import {MeshMyNodeComponent} from "../LowerHeaders/meshtastic/mesh-my-node.component"; import {MeshMyNodeComponent} from "../LowerHeaders/meshtastic/mesh-my-node.component";
import {MeshListNodeComponent} from "../LowerHeaders/meshtastic/mesh-list-node.component"; import {MeshListNodeComponent} from "../LowerHeaders/meshtastic/mesh-list-node.component";
import {IOService} from "../../services/IOService"; import {IOService} from "../../services/IOService";
import {MeshChannelComponent} from "../LowerHeaders/meshtastic/mesh-channel.component"; import {MatSnackBar} from "@angular/material/snack-bar";
import {MeshMessagesComponent} from "../LowerHeaders/meshtastic/mesh-messages.component";
@Component({ @Component({
selector: "app-meshtastic-header", selector: "app-meshtastic-header",
@ -44,17 +45,19 @@ export class MeshtasticHeaderComponent extends AbsNavsHeaderComponent implements
constructor(public meshtastic: MeshtasticService, constructor(public meshtastic: MeshtasticService,
route: ActivatedRoute, route: ActivatedRoute,
router: Router, router: Router,
public io: IOService) { public io: IOService,
protected snack: MatSnackBar) {
super(route, router); super(route, router);
this.meshtastic.channelChange.subscribe( this.meshtastic.channelChange.subscribe(
(listen:any) => { (listen:any) => {
let base = [ let base = [
{name: "my", path: "my", action: () => {}, component: MeshMyNodeComponent.name}, {name: "my", path: "my", action: () => {}, component: MeshMyNodeComponent.name},
{name: "list", path: "list", action: () => {}, component: MeshListNodeComponent.name} {name: "list", path: "list", action: () => {}, component: MeshListNodeComponent.name},
{name: "messages", path: "messages/-1", action: () => {}, component: MeshMessagesComponent.name}
] ]
base.push(... base.push(...
this.meshtastic.channels.map((ch) => { this.meshtastic.channels.map((ch) => {
return {name: `${ch.name}`, path: `channel/${ch.index}`, action: () => {}, component: MeshChannelComponent.name} return {name: `${ch.name}`, path: `messages/${ch.index}`, action: () => {}, component: MeshMessagesComponent.name}
})); }));
console.log(base); console.log(base);
super.navs = base; super.navs = base;
@ -92,6 +95,14 @@ export class MeshtasticHeaderComponent extends AbsNavsHeaderComponent implements
} }
override ngOnInit(): void { override ngOnInit(): void {
this.meshtastic.messagesChange.subscribe(//todo mb to main app view
(msg: MeshMessage) => {
if (msg.fr0m == this.meshtastic.meshId) return;
const userName = this.meshtastic.getNodeName(msg.fr0m)
this.snack.open(`${userName}: ${msg.msg}`, undefined, {duration: 2000})
}
)
this.sub = this.io.keys.subscribe((code) => { this.sub = this.io.keys.subscribe((code) => {
//if (this.apps.inApp) return; //if (this.apps.inApp) return;
super.choiceHeader(code); super.choiceHeader(code);

48
pipboyUI/src/app/components/abstract/AbsListSelect.ts

@ -2,12 +2,12 @@ import {Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChildren} from
import {Subscription} from "rxjs"; import {Subscription} from "rxjs";
@Component({template:""}) @Component({template:""})
export abstract class AbsListSelect implements OnInit, OnDestroy{ export abstract class AbsListSelect<LEFT, RIGHT> implements OnInit, OnDestroy{
elements: any[] = [] elements: LEFT[] = []
selected: any; selected?: LEFT;
elementsRight: any = [] elementsRight: RIGHT[] = []
selectedRight:any; selectedRight?:RIGHT;
currentSide: "RIGHT" | "LEFT" = "LEFT"; currentSide: "RIGHT" | "LEFT" = "LEFT";
@ -17,7 +17,7 @@ export abstract class AbsListSelect implements OnInit, OnDestroy{
isSelected(el:any):boolean { isSelected(el:any):boolean {
const res = el == this.selected; const res = el == this.selected;
if (res && this.menuItems) { if (res && this.menuItems && this.selected != undefined) {
const itemsArray = this.menuItems.toArray(); const itemsArray = this.menuItems.toArray();
const selectedIndex = this.elements.indexOf(this.selected); const selectedIndex = this.elements.indexOf(this.selected);
if (selectedIndex !== -1 && itemsArray[selectedIndex]) { if (selectedIndex !== -1 && itemsArray[selectedIndex]) {
@ -31,7 +31,7 @@ export abstract class AbsListSelect implements OnInit, OnDestroy{
return res; return res;
} }
choiceItem(code: string) { choiceItem(code: string):boolean {
switch (this.currentSide) { switch (this.currentSide) {
case "LEFT": return this.choiceLeft(code) case "LEFT": return this.choiceLeft(code)
case "RIGHT": return this.choiceRight(code) case "RIGHT": return this.choiceRight(code)
@ -42,44 +42,54 @@ export abstract class AbsListSelect implements OnInit, OnDestroy{
if (this.sub) this.sub.unsubscribe(); if (this.sub) this.sub.unsubscribe();
} }
choiceLeft(code: string) { choiceLeft(code: string):boolean {
if (this.currentSide != "LEFT") return; if (this.currentSide != "LEFT") return false;
let currentIdx = this.elements.indexOf(this.selected); let currentIdx = this.selected == undefined ? 0 : this.elements.indexOf(this.selected);
switch (code) { switch (code) {
case 'KeyS': { case 'KeyS': {
if (currentIdx + 1 >= this.elements.length) currentIdx = -1; if (currentIdx + 1 >= this.elements.length) currentIdx = -1;
this.selected = this.elements[currentIdx + 1]; this.selected = this.elements[currentIdx + 1];
break; return true;
} }
case 'KeyW': { case 'KeyW': {
if (currentIdx - 1 < 0) currentIdx = this.elements.length; if (currentIdx - 1 < 0) currentIdx = this.elements.length;
this.selected = this.elements[currentIdx - 1]; this.selected = this.elements[currentIdx - 1];
break; return true;
} }
case 'Enter': { case 'Enter': {
this.selected.action() if (this.selected != undefined)
{ // @ts-ignore
this.selected.action()
}
return false;
} }
} }
return false;
} }
choiceRight(code: string) { choiceRight(code: string): boolean {
if (this.currentSide != "RIGHT") return; if (this.currentSide != "RIGHT") return false;
let currentIdx = this.elementsRight.indexOf(this.selectedRight); let currentIdx = this.selectedRight == undefined ? 0 : this.elementsRight.indexOf(this.selectedRight);
switch (code) { switch (code) {
case 'KeyS': { case 'KeyS': {
if (currentIdx + 1 >= this.elementsRight.length) currentIdx = -1; if (currentIdx + 1 >= this.elementsRight.length) currentIdx = -1;
this.selectedRight = this.elementsRight[currentIdx + 1]; this.selectedRight = this.elementsRight[currentIdx + 1];
break; return true;
} }
case 'KeyW': { case 'KeyW': {
if (currentIdx - 1 < 0) currentIdx = this.elementsRight.length; if (currentIdx - 1 < 0) currentIdx = this.elementsRight.length;
this.selectedRight = this.elementsRight[currentIdx - 1]; this.selectedRight = this.elementsRight[currentIdx - 1];
break; return true;
} }
case 'Enter': { case 'Enter': {
this.selectedRight.action() if (this.selectedRight != undefined)
{ // @ts-ignore
this.selectedRight.action()
}
return false;
} }
} }
return false;
} }
ngOnInit(): void { ngOnInit(): void {

81
pipboyUI/src/app/services/MeshtasticService.ts

@ -1,7 +1,8 @@
import {Injectable} from "@angular/core"; import {Injectable} from "@angular/core";
import {WebSocketSubject} from "rxjs/internal/observable/dom/WebSocketSubject"; import {WebSocketSubject} from "rxjs/internal/observable/dom/WebSocketSubject";
import {webSocket} from "rxjs/webSocket"; import {webSocket} from "rxjs/webSocket";
import {BehaviorSubject, delay, retryWhen, tap} from "rxjs"; import {BehaviorSubject, delay, Observable, retryWhen, tap} from "rxjs";
import {HttpClient} from "@angular/common/http";
export const WS_EVENT_CHANNEL = 0; export const WS_EVENT_CHANNEL = 0;
export const WS_EVENT_NODE = 1; export const WS_EVENT_NODE = 1;
@ -20,31 +21,96 @@ export const ERR = 3;
export const RECONNECT = 4; export const RECONNECT = 4;
export const WS_ERR = 5; export const WS_ERR = 5;
interface MeshNodeShort { export const PUB_CH = 0xFFFFFFFF;
export interface MeshNodeShort {
id: number; id: number;
name: string; name: string;
} }
interface MeshChannel { export interface MeshChannel {
index: number; index: number;
name: string; name: string;
} }
export interface MeshUser {
id?: String;
long_name?: String;
short_name?:String;
}
export interface MeshNode {
num?: number;
user?: MeshUser;
snr?: number;
hops_away?: number;
last_heard?: number;
}
export interface MeshMessage {
fr0m: number;
to:number;
msg?:string;
id?:number;
rx_time?:number;
rx_snr?:number;
hop_limit?:number;
rx_rssi?:number;
channel?:number;
append_time: number;
}
export interface MeshNewMessage {
txt: string;
node_id?: number|null;
channel_id?:number;
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class MeshtasticService { export class MeshtasticService {
private meshRoute = "mesh/api"
private socket$?: any; private socket$?: any;
public channels: MeshChannel[] = []; public channels: MeshChannel[] = [];
public channelChange = new BehaviorSubject(0); public channelChange = new BehaviorSubject(0);
public nodes: MeshNodeShort[] = []; public nodes: MeshNodeShort[] = [];
public nodesChange = new BehaviorSubject(0); public nodesChange = new BehaviorSubject(0);
public messages = new BehaviorSubject('{}') public messagesChange = new BehaviorSubject({} as MeshMessage);
public messages: MeshMessage[] = [];
public state: number = NOT_CONNECTED; public state: number = NOT_CONNECTED;
public meshId: number = 0; public meshId: number = 0;
public getNodeInfo(id: number): Observable<MeshNode> {
return this.http.get(`${this.meshRoute}/nodes/${id}`)
}
constructor() { public getNodeName(id: number): string {
const name:string|undefined = this.nodes.filter(n => n.id == id).map(n => n.name).pop();
return name ? name : "Unknown";
}
public getMsgTo(msg: MeshMessage): string {
if (msg.to == PUB_CH) {
//chanel
const chName: string|undefined = this.channels
.filter((ch) => ch.index == msg.channel)
.map((ch) => ch.name)
.pop();
return chName == undefined ? "CH_U" : chName;
} else return "DM"
}
public sendMessage(txt: string, node_id: number|null =null, channel_id: number = 0) {
let payload: MeshNewMessage = {
txt: txt,
node_id: node_id,
channel_id: channel_id
}
return this.http.post(`${this.meshRoute}/messages`, payload);
}
constructor(private http: HttpClient) {
this.connect(); this.connect();
} }
@ -69,6 +135,11 @@ export class MeshtasticService {
this.socket$.subscribe( this.socket$.subscribe(
(parsed: {type: number, event: number, data: any}) => { (parsed: {type: number, event: number, data: any}) => {
switch (parsed.event) { switch (parsed.event) {
case WS_EVENT_MESSAGE: {
this.messages.push(parsed.data);
this.messagesChange.next(parsed.data);
break;
}
case WS_EVENT_MYID: { case WS_EVENT_MYID: {
this.meshId = parsed.data; this.meshId = parsed.data;
break; break;

11
pipboyUI/src/styles.scss

@ -85,6 +85,17 @@ body {
width: $screen-width !important; width: $screen-width !important;
} }
.mat-snack-bar-container {
box-shadow: unset !important;
background: black;
color: unset !important;
border: 2px $primary-color solid;
}
.mat-progress-spinner, .mat-spinner, circle {
stroke: $primary-color !important;
}
.mat-dialog-content { .mat-dialog-content {
max-height: 100vh !important; max-height: 100vh !important;
margin: 0 0px !important; margin: 0 0px !important;

Loading…
Cancel
Save