1/* 2mqtt.mjs - for patterning the internet of things from strudel 3Copyright (C) 2022 Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/packages/serial/serial.mjs> 4This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. 5*/ 6 7import { Pattern, isPattern, createParams } from '@strudel/core'; 8import Paho from 'paho-mqtt'; 9 10const connections = {}; 11 12// Handle connection loss 13function onConnectionLost(responseObject) { 14 if (responseObject.errorCode !== 0) { 15 console.error(' mqtt connection lost: ', responseObject.errorMessage); 16 } 17} 18 19// Handle received messages 20function onMessageArrived(message) { 21 console.log('incoming mqtt message: ', message.payloadString); // prettier-ignore 22} 23 24function onFailure(err) { 25 console.error('Connection failed: ', err); 26 if (typeof window !== 'undefined') { 27 document.cookie = 'mqtt_pass='; 28 } 29} 30 31Pattern.prototype.mqtt = function ( 32 username = undefined, 33 password = undefined, 34 topic = undefined, 35 host = 'wss://localhost:8883/', 36 client = undefined, 37 latency = 0, 38 add_meta = true, 39) { 40 const key = host + '-' + client; 41 let password_entered = false; 42 43 function onConnect() { 44 console.log('Connected to mqtt broker'); 45 if (password_entered) { 46 document.cookie = 'mqtt_pass=' + password; 47 } 48 } 49 50 let cx; 51 if (connections[key]) { 52 cx = connections[key]; 53 } else { 54 if (!client) { 55 client = 'strudel-' + String(Math.floor(Math.random() * 1000000)); 56 } 57 cx = new Paho.Client(host, client); 58 connections[key] = cx; 59 cx.onConnectionLost = onConnectionLost; 60 cx.onMessageArrived = onMessageArrived; 61 const props = { 62 onSuccess: onConnect, 63 onFailure: onFailure, 64 }; 65 66 if (username) { 67 props.userName = username; 68 if (typeof password === 'undefined' && typeof window !== 'undefined') { 69 const cookie = /mqtt_pass=(\w+)/.exec(window.document.cookie); 70 if (cookie) { 71 password = cookie[1]; 72 } 73 if (typeof password === 'undefined') { 74 password = prompt('Please enter MQTT server password'); 75 password_entered = true; 76 } 77 } 78 79 props.password = password; 80 } 81 cx.connect(props); 82 } 83 return this.withHap((hap) => { 84 const onTrigger = (hap, currentTime, cps, targetTime) => { 85 let msg_topic = topic; 86 if (!cx || !cx.isConnected()) { 87 return; 88 } 89 let message = ''; 90 if (typeof hap.value === 'object') { 91 let value = hap.value; 92 93 // Try to take topic from pattern if it's not set 94 if (typeof msg_topic === 'undefined' && 'topic' in value) { 95 msg_topic = value.topic; 96 if (Array.isArray(msg_topic)) { 97 msg_topic = msg_topic.join('/'); 98 } 99 msg_topic = '/' + msg_topic; 100 } 101 if (add_meta) { 102 const duration = hap.duration.div(cps); 103 value = { ...value, duration: duration.valueOf(), cps: cps }; 104 } 105 message = JSON.stringify(value); 106 } else { 107 message = hap.value; 108 } 109 message = new Paho.Message(message); 110 message.destinationName = msg_topic; 111 112 const offset = (targetTime - currentTime + latency) * 1000; 113 114 window.setTimeout(function () { 115 cx.send(message); 116 }, offset); 117 }; 118 return hap.setContext({ ...hap.context, onTrigger, dominantTrigger: true }); 119 }); 120}; 121 122// This adds the 'move' and 'motor' commands to strudel 123export const { move, motor } = createParams('move', 'motor'); 124window.move = move; 125window.motor = motor; 126// This adds the 'robot' command 127Pattern.prototype.robot = function (robot_id, address = 'ws://192.168.8.248:9001/mqtt') { 128 return this.mqtt(undefined, undefined, '/move/' + robot_id, address); 129};