github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/static_source/admin/src/api/stream.ts (about) 1 import {ConstantBackoff, Websocket, WebsocketBuilder} from 'websocket-ts'; 2 import {EventHTML5Notify} from '@/api/types'; 3 import {useCache} from "@/hooks/web/useCache"; 4 import {UUID} from "uuid-generator-ts"; 5 import {ref} from "vue"; 6 7 const {wsCache} = useCache() 8 9 export const streamStatus = ref<'online' | 'offline'>('offline'); 10 11 class Stream { 12 private ws: Websocket | null = null; 13 protected subscribers: { [key: string]: any }; 14 15 constructor() { 16 this.subscribers = {}; 17 } 18 19 connect(url: string, accessToken: string): void { 20 if (this.ws) { 21 return; 22 } 23 url = url.replace("https", "wss") 24 url = url.replace("http", "ws") 25 url = url + '/v1/ws?access_token=' + accessToken; 26 27 if (wsCache.get('serverId')) { 28 url = url + '&server_id=' + wsCache.get('serverId'); 29 } 30 31 this.ws = new WebsocketBuilder(url) 32 .onOpen((ws: Websocket, ev: Event) => this.onOpen(ws, ev, accessToken)) 33 .onClose((ws: Websocket, ev: CloseEvent) => this.onClose(ws, ev)) 34 .onError((ws: Websocket, ev: Event) => this.error(ws, ev)) 35 .onMessage((ws: Websocket, ev: MessageEvent) => this.onMessage(ws, ev)) 36 .withBackoff(new ConstantBackoff(1000)) // 1000ms = 1s 37 .onRetry((i, ev) => { 38 console.log('retry'); 39 }) 40 .build(); 41 } 42 43 disconnect(): void { 44 if (!this.ws) { 45 return; 46 } 47 this.ws.close(); 48 this.ws = null; 49 } 50 51 private error(ws: Websocket, ev: Event): any { 52 streamStatus.value = 'offline' 53 console.log('error'); 54 } 55 56 private onClose(ws: Websocket, ev: CloseEvent): any { 57 streamStatus.value = 'offline' 58 console.log('closed'); 59 } 60 61 private onOpen(ws: Websocket, ev: Event, accessToken: string): any { 62 streamStatus.value = 'online' 63 console.log('opened'); 64 ws.send(JSON.stringify({body: btoa('init'), access_token: accessToken})); 65 ws.send(JSON.stringify({ 66 id: UUID.createUUID(), 67 query: 'event_get_server_version', 68 })); 69 } 70 71 private onMessage(ws: Websocket, ev: MessageEvent): any { 72 let m: StreamResponse; 73 try { 74 const result = JSON.parse(ev.data); 75 m = result; 76 const body: any = JSON.parse(atob(result.body)); 77 m.body = body; 78 } catch { 79 console.debug('from the stream came a string value'); 80 return; 81 } 82 83 switch (m.query) { 84 case 'html5_notify': 85 return this.html5Notify(ev.data) 86 } 87 88 if (!this.subscribers) { 89 return; 90 } 91 92 if (m.query in this.subscribers) { 93 for (const id in this.subscribers[m.query]) { 94 this.subscribers[m.query][id](m.body); 95 } 96 } 97 } 98 99 public send(data: any) { 100 if (this.ws) { 101 this.ws.send(JSON.stringify(data)); 102 } 103 } 104 105 public subscribe(name: string, id: string, f: any) { 106 console.debug('subscribe', name, id); 107 if (this.subscribers[name] === undefined) { 108 this.subscribers[name] = {}; 109 } 110 this.subscribers[name][id] = f; 111 } 112 113 public unsubscribe(name: string, id: string) { 114 console.debug('unsubscribe', name, id); 115 if (this.subscribers[name] !== undefined) { 116 if (this.subscribers[name][id] !== undefined) { 117 delete this.subscribers[name][id]; 118 } 119 } 120 } 121 122 public notify(event: EventHTML5Notify) { 123 if (!('Notification' in window)) { 124 console.warn('the browser does not support HTML notifications, it needs to be updated'); 125 return; 126 } 127 if (Notification.permission === 'granted') { 128 new Notification(event.title, event.options); 129 return; 130 } 131 if (Notification.permission !== 'denied') { 132 Notification.requestPermission(function(permission) { 133 if (permission === 'granted') { 134 new Notification(event.title, event.options); 135 } 136 }); 137 } 138 } 139 140 private html5Notify(data: string) { 141 const {body} = JSON.parse(data); 142 const msg: EventHTML5Notify = JSON.parse(atob(body)); 143 // console.log(msg) 144 this.notify(msg); 145 } 146 } 147 148 const stream = new Stream(); 149 150 export default stream; 151 152 export interface StreamResponse { 153 id: string; 154 query: string; 155 body: string; 156 } 157 158 export interface StreamRequest { 159 id: string; 160 query: string; 161 body: string; 162 }