├── .gitignore ├── Hardware └── README.md ├── LICENSE ├── README.md ├── Resource ├── agent_example.png ├── click_manage_palette.png ├── dashboard.png ├── dashboard_nodes.png ├── deploy_flow.png ├── flowchart.png ├── home_assistant_ws.png ├── import_flow.png ├── import_local_flow_file.png ├── node_red_flows.png └── select_install.png └── Software ├── README.md ├── llm ├── asr.py ├── chatbot.py ├── ha.py ├── local_llm.py ├── mqtt.py ├── readme.md ├── sources │ └── text-generation-inference.png └── tts.py └── node-red ├── README.md ├── grove_vision_ai_flows.json ├── main_flow.json └── test.glb /.gitignore: -------------------------------------------------------------------------------- 1 | ## ignore paths ## 2 | 3 | /.idea/ 4 | 5 | 6 | -------------------------------------------------------------------------------- /Hardware/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Hardware/README.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Seeed-Projects 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LocalJARVIS Project (🚧WIP🚧) 2 | 3 | This project takes off with the Application Engineers at Seeed Studio embarking on a journey to leverage the advanced capabilities of the Jetson Orin AGX. Our mission is to deploy it for the operation of a Local Large Language Model (LLM) embedded with a Langchain agent. The objective is to evolve our office environment into a cutting-edge, GenAI-infused AIoT workspace, further enhanced by the integration of automation with the Home Assistant project. The goal is to establish an open-source framework based on RAG for building a local LLM that interacts with Home Assistant API. This endeavor seeks not only to elevate the workplace atmosphere but also to decrease energy consumption, aligning with Sustainable Development Goals (SDG). 4 | 5 | This comprehensive project is divided into several crucial segments, each indispensable for the effective real-world deployment of this groundbreaking upgrade. 6 | 7 | Here is the flowchart of the whole system: 8 | 9 |

10 | 11 |

12 | 13 | 1. Voice Input: Users engage with the system by initiating conversations via ReSpeaker. 14 | 2. Voice Processing: The ReSpeaker sends these commands to NVIDIA® Riva software component that recognizes speech (ASR - Automatic Speech Recognition), NVIDIA® Riva is the speech multilingual speech and translation AI software development kit developed by Nvidia, this Nvidia Riva service is running locally on the Nvidia Jetson devices, in this case we are uing a powerfull Orin AGX . After recognizing the speech and Riva turns it into input prompts for further processing. 15 | 3. Local Processing or Cloud Processing: The input can be processed locally using a server called Jetson Orin AGX, or it can be sent to the cloud through OpenAI's API for language understanding and response generation(to be noted: We maintain the use of OpenAI API for its significantly better performance and accuracy over the locally-run llama-2-7b models. Nonetheless, our ultimate aim is to transition to a fully local setup. We are confident in our ability to enhance the performance of local LLMs to achieve this objective.) 16 | 4. Decision Making: 17 | * If the input requires controlling tools, it goes to through a LangChain process, which decides if an action needs to be taken (like turning on a light).
18 | For example:
19 | User: Hi Javis!
20 | Agent: bla bla bla
21 | 22 | * If no action is needed, it proceeds to generate a response directly. 23 | 5. Home Automation Interaction: If an action is needed, LangChain agent communicates with Home Assistant through Home Assistant API to control and perform the task on smart appliances (These are the devices being controlled by the system, like lights, thermostats, or any other connected smart home devices, the action will for example be turn on off the lights, control the smart thermostat, set a to-do task, or even orders things from the online shop(amazon, aliexpress) and etc 24 | 6. Feedback: The system provides feedback of the states of the executes a control action. 25 | 7. Display Information: Display the information via Node-RED, like the current state of home entities (like temperature sensors), time, or weather reports, etc. 26 | 27 | ## Hardware List: 28 | 29 | * [Seeed Studio AGX Orin™](https://www.seeedstudio.com/NVIDIArJetson-AGX-Orintm-64GB-Developer-Kit-p-5641.html) 30 | * [15.6inch Monitor](https://www.seeedstudio.com/15-6-Inch-IPS-Portable-Monitor-p-5757.html) 31 | * [Home Assistant Green](https://www.seeedstudio.com/Home-Assistant-Green-p-5792.html) 32 | 33 | ## Setup Guide 34 | 35 | ### NVIDIA® Riva 36 | 37 | The instruction on how to setup could found [here](https://wiki.seeedstudio.com/Local_Voice_Chatbot/) 38 | 39 | ### LocalLLM 40 | 41 | The instruction on setup Local LLM and the LangChain agent are listed [here](https://github.com/Seeed-Projects/LocalJARVIS/tree/main/Software/llm) 42 | 43 | Here is an example of the LangChain action: 44 | 45 |

46 | 47 |

48 | 49 | ### Dashboard 50 | 51 |

52 | 53 |

54 | 55 | The instruction on how to setup could found [here](https://github.com/Seeed-Projects/LocalJARVIS/tree/main/Software/node-red) 56 | 57 | ### Home Assistant 58 | 59 | the instruction on how to setup could found [here]() --comming soon 🚧 60 | 61 | ## To-Do/Wish list 🚧 62 | 63 | ### Project To-Do 64 | 65 | - [ ]FIX Project README under construction part 66 | - [ ]Collect Ideas 67 | 68 | ### Wish List 69 | 70 | - [ ]Implement and test RAG 71 | 72 | ## Contribution 73 | 74 | Join Us in Shaping this LocalJARVIS project! 🌟 75 | 76 | Hello, Open Source and Local llm Enthusiasts! 77 | 78 | We're excited to invite you to contribute to this project, As we navigate through the journey of building something impactful, your expertise, passion, and creativity can make a significant difference. 79 | 80 | Why Contribute to LocalJARVIS? 81 | 82 | Learn and Grow: Dive deep into [technology stack, e.g., Local LLM, Nvidia Riva, LangChain, Home Assistant, Python, Node-RED,JavaScript, etc.] and best practices in software development. Whether you're a seasoned developer or just starting, there's always something new to learn. 83 | Community and Collaboration: Join a vibrant community of like-minded individuals. Collaborate, share ideas, and get feedback. It's a great way to network and grow together. 84 | Make an Impact: Your contributions directly help in making [Project Name] better and more useful for everyone. It's an opportunity to contribute to something that you and many others will benefit from. 85 | Recognition: All contributors will be recognized for their efforts. We celebrate contributions of all sizes and acknowledge the hard work of our community members. 86 | How Can You Contribute? 87 | 88 | We welcome contributions of all kinds, from code improvements, bug fixes, documentation updates, to feature suggestions and more. Here's how you can get started: 89 | 90 | Check Out the Issues: Browse through our [issues tab](https://github.com/Seeed-Projects/LocalJARVIS/issues) to find something that interests you. We tag issues with 'good first issue' for newcomers! 91 | Propose Your Idea: Have an idea or a feature request? Open a new issue and let's discuss how it can fit into the project. 92 | Submit a Pull Request: Ready to get your hands dirty? Fork the repository, make your changes, and submit a pull request. Don't forget to follow our contribution guidelines! 93 | Getting Started 94 | 95 | Contribution Guidelines: [link to CONTRIBUTING.md]() -- Comming soon 🚧 96 | 97 | Need Help? 98 | 99 | We understand that contributing to an open-source project can be daunting for newcomers. We encourage you to reach out if you have any questions or need guidance. Join our [Discord/Community Channel]/ Our team and community members are here to help! 100 | 101 | Thank you for considering contributing. Your support is what drives the success of this project. We can't wait to see what we can build together! 102 | 103 | Happy Coding! 🍻🍻🍻 104 | 105 | The Seeed Studio Application Engineering Team -------------------------------------------------------------------------------- /Resource/agent_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/agent_example.png -------------------------------------------------------------------------------- /Resource/click_manage_palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/click_manage_palette.png -------------------------------------------------------------------------------- /Resource/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/dashboard.png -------------------------------------------------------------------------------- /Resource/dashboard_nodes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/dashboard_nodes.png -------------------------------------------------------------------------------- /Resource/deploy_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/deploy_flow.png -------------------------------------------------------------------------------- /Resource/flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/flowchart.png -------------------------------------------------------------------------------- /Resource/home_assistant_ws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/home_assistant_ws.png -------------------------------------------------------------------------------- /Resource/import_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/import_flow.png -------------------------------------------------------------------------------- /Resource/import_local_flow_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/import_local_flow_file.png -------------------------------------------------------------------------------- /Resource/node_red_flows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/node_red_flows.png -------------------------------------------------------------------------------- /Resource/select_install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Resource/select_install.png -------------------------------------------------------------------------------- /Software/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Software/README.md -------------------------------------------------------------------------------- /Software/llm/asr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import time 3 | import threading 4 | import riva.client 5 | import riva.client.audio_io 6 | 7 | 8 | class ASR(threading.Thread): 9 | 10 | def __init__(self, auth=None, input_device=None, sample_rate_hz=16000, audio_chunk=1600, audio_channels=1, 11 | automatic_punctuation=True, verbatim_transcripts=True, profanity_filter=False, 12 | language_code='en-US', boosted_lm_words=None, boosted_lm_score=4.0, callback=None): 13 | super(ASR, self).__init__() 14 | 15 | assert auth is not None, f"Invalid parameter: {auth}" 16 | self.asr_service = riva.client.ASRService(auth) 17 | 18 | self.input_device = input_device 19 | self.sample_rate_hz = sample_rate_hz 20 | self.audio_chunk = audio_chunk 21 | self.callback = callback 22 | if self.callback is None: 23 | self.callback = self.callback_example 24 | 25 | self.asr_config = riva.client.StreamingRecognitionConfig( 26 | config=riva.client.RecognitionConfig( 27 | encoding=riva.client.AudioEncoding.LINEAR_PCM, 28 | language_code=language_code, 29 | max_alternatives=1, 30 | profanity_filter=profanity_filter, 31 | enable_automatic_punctuation=automatic_punctuation, 32 | verbatim_transcripts=verbatim_transcripts, 33 | sample_rate_hertz=sample_rate_hz, 34 | audio_channel_count=audio_channels, 35 | ), 36 | interim_results=True, 37 | ) 38 | 39 | riva.client.add_word_boosting_to_config(self.asr_config, boosted_lm_words, boosted_lm_score) 40 | self.mute_flag = False 41 | self.stop_flag = False 42 | 43 | def run(self): 44 | with riva.client.audio_io.MicrophoneStream( 45 | self.sample_rate_hz, 46 | self.audio_chunk, 47 | device=self.input_device, 48 | ) as audio_chunk_iterator: 49 | responses = self.asr_service.streaming_response_generator( 50 | audio_chunks=audio_chunk_iterator, streaming_config=self.asr_config 51 | ) 52 | for response in responses: 53 | if self.stop_flag: 54 | audio_chunk_iterator.close() 55 | print("ASR Server Shutdown!") 56 | if self.get_mute_state(): 57 | continue 58 | else: 59 | if not response.results: 60 | continue 61 | self.callback(response) 62 | 63 | @staticmethod 64 | def callback_example(response): 65 | try: 66 | for result in response.results: 67 | if not result.alternatives: 68 | continue 69 | transcript = result.alternatives[0].transcript 70 | if result.is_final: 71 | print("## " + transcript) 72 | print(f"Confidence:{result.alternatives[0].confidence:9.4f}" + "\n") 73 | return True 74 | else: 75 | print(">> " + transcript) 76 | print(f"Stability:{result.stability:9.4f}" + "\n") 77 | finally: 78 | pass 79 | 80 | def get_mute_state(self): 81 | return self.mute_flag 82 | 83 | @staticmethod 84 | def list_devices(): 85 | riva.client.audio_io.list_input_devices() 86 | 87 | 88 | if __name__ == '__main__': 89 | 90 | ASR.list_devices() 91 | 92 | # _riva_server = "192.168.49.74:50051" 93 | # _input_device = 16 94 | # _sample_rate_hz = 16000 95 | # _auth = riva.client.Auth(uri=_riva_server) 96 | # asr = ASR(_auth, input_device=_input_device, sample_rate_hz=_sample_rate_hz) 97 | # asr.start() 98 | # 99 | # while True: 100 | # print(">>>> The main line into the ongoing. <<<<") 101 | # time.sleep(5) 102 | # break 103 | # 104 | # asr.stop_flag = True 105 | # print(">>>> waiting... <<<<") 106 | # time.sleep(5) 107 | -------------------------------------------------------------------------------- /Software/llm/chatbot.py: -------------------------------------------------------------------------------- 1 | # Description : LangChain + OpenAI API / Text-Inference-Generation + Riva / WebUI + MQTT. 2 | 3 | import sys 4 | import pyaudio 5 | import argparse 6 | 7 | from langchain.chains.conversation.memory import ConversationBufferMemory 8 | from langchain.llms import OpenAI 9 | from langchain.agents import Tool, initialize_agent, AgentType 10 | import time 11 | 12 | import riva.client 13 | import riva.client.audio_io 14 | 15 | from asr import ASR 16 | from tts import TTS 17 | from local_llm import CustomLLM 18 | from ha import HomeAssistant 19 | from mqtt import MQTT 20 | 21 | 22 | def parse_args(): 23 | parser = argparse.ArgumentParser(description="Control smart furniture by LLM") 24 | parser.add_argument( 25 | "--openai_api", type=str, help="Get API Key from: https://openai.com/blog/openai-api", 26 | ) 27 | parser.add_argument( 28 | "--text_inference_server", type=str, default='http://192.168.88.104:8899/generate', 29 | help="URL to the local large model inference service", 30 | ) 31 | parser.add_argument( 32 | "--riva_server", type=str, help="URL of the Riva server, For example: '192.168.49.104:50051'.", 33 | ) 34 | 35 | parser.add_argument("--ha_access_token", type=str, help="Access token of HomeAssistant.") 36 | parser.add_argument("--ha_base_url", type=str, default='http://aeassistant.lan:8123/', help="URL of the HomeAssistant") 37 | 38 | parser.add_argument("--mqtt_broker_ip", type=str, default='192.168.88.104', help="IP of mqtt broker") 39 | parser.add_argument("--mqtt_broker_port", type=str, default='1883', help="Port of mqtt broker") 40 | 41 | args = parser.parse_args() 42 | return args 43 | 44 | 45 | class SeeeBot: 46 | def __init__( 47 | self, openai_api=None, 48 | text_inference_server='http://192.168.88.104:8899/generate', 49 | riva_server=None, 50 | asr_input_device_id=None, 51 | asr_sample_rate_hz=None, 52 | tts_output_device_id=None, 53 | tts_sample_rate_hz=None, 54 | mqtt_broker_ip="192.168.88.104", 55 | mqtt_broker_port="1883", 56 | ha_access_token='', 57 | ha_base_url='http://aeassistant.lan:8123/' 58 | ): 59 | # whether to activate MQTT. 60 | if mqtt_broker_ip is not None: 61 | self.mqtt_client = MQTT(mqtt_broker=mqtt_broker_ip, mqtt_port=mqtt_broker_port) 62 | 63 | # Create tools for the large langchain agent. 64 | # Please refer to the example below, add the tool in HomeAssistant, and declare it in the Tools section below. 65 | self.ha = HomeAssistant(ha_access_token, ha_base_url) 66 | tools = [ 67 | Tool( 68 | name='turn_on_air_conditioner', 69 | func=self.ha.turn_on_air_conditioner, 70 | description=f"""When you need to turn on the air conditioner, please execute this tool.""" 71 | ), 72 | Tool( 73 | name='turn_off_air_conditioner', 74 | func=self.ha.turn_off_air_conditioner, 75 | description=f"""When you need to turn off the air conditioner, please execute this tool.""" 76 | ) 77 | ] 78 | 79 | # Instantiate the large language model agent by langchain. 80 | self.memory = ConversationBufferMemory(memory_key='chat_history') 81 | if openai_api is not None: 82 | llm = OpenAI(openai_api_key=openai_api, temperature=0.7) 83 | self.agent = initialize_agent( 84 | tools, llm, agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=self.memory, 85 | handle_parsing_errors=True, max_iterations=3, 86 | agent_kwargs={ 87 | "prefix": "Assume you are a smart chatbot named Frank. You can adjust the status of smart home devices based on human input."} 88 | ) 89 | elif text_inference_server is not None: 90 | llm = CustomLLM(url=text_inference_server, temperature=0.1, max_new_tokens=128) 91 | 92 | def _handle_error(error) -> str: 93 | return f"AI : {error}" 94 | 95 | self.agent = initialize_agent( 96 | tools, llm, agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=self.memory, 97 | max_iterations=3, handle_parsing_errors=_handle_error, 98 | agent_kwargs={ 99 | "prefix": "Assume you are a smart chatbot named Frank. You can adjust the status of smart home devices based on human input."} 100 | ) 101 | else: 102 | print('No model to load!') 103 | sys.exit(0) 104 | print(f"Built-in prompt template:\n >>> \n {self.agent.agent.llm_chain.prompt.template} \n <<<") 105 | 106 | # Human-computer interaction tool 107 | self.auth = None 108 | self.chat_history = [] 109 | if riva_server is not None: 110 | self.auth = riva.client.Auth(uri=riva_server) 111 | 112 | if asr_input_device_id is None: 113 | input_device, output_device = self.get_riva_devices_list() 114 | asr_input_device_id = input_device['index'] 115 | asr_sample_rate_hz = int(input_device['defaultSampleRate']) 116 | tts_output_device_id = output_device['index'] 117 | tts_sample_rate_hz = int(output_device['defaultSampleRate']) 118 | print(f"Default ASR Device: {asr_input_device_id}, {asr_sample_rate_hz}") 119 | print(f"Default TTS Device: {tts_output_device_id}, {tts_sample_rate_hz}") 120 | 121 | self.asr = ASR( 122 | self.auth, input_device=asr_input_device_id, sample_rate_hz=asr_sample_rate_hz, 123 | callback=self.on_asr_transcript 124 | ) 125 | self.tts = TTS(self.auth, output_device=tts_output_device_id, sample_rate_hz=tts_sample_rate_hz) 126 | self.asr_interrupt_flag = None 127 | self.asr_mute = False 128 | 129 | self.wakeup = False 130 | self.wakeup_world = ['Frank', 'frank'] 131 | self.sleep_world = ['goodbye', 'Goodbye', 'good bye', 'Good bye', 'Good Bye'] 132 | 133 | def run(self): 134 | if self.auth is not None: 135 | self.run_riva() 136 | else: 137 | self.run_webui() 138 | 139 | def run_riva(self): 140 | self.asr.start() 141 | time.sleep(0.5) 142 | while True: 143 | if self.asr_interrupt_flag is not None: 144 | self.asr_mute = True 145 | if self.wakeup: 146 | for world in self.sleep_world: 147 | if world in self.asr_interrupt_flag[0]: 148 | self.wakeup = False 149 | self.send_mqtt_message("/seeebot", 'Good bye, have a good day.') 150 | self.tts.output_audio('Goodbye, Have a nice day.') 151 | self.send_mqtt_message("/bye", 'bye') 152 | break 153 | break 154 | else: 155 | for world in self.wakeup_world: 156 | if world in self.asr_interrupt_flag[0]: 157 | self.wakeup = True 158 | self.send_mqtt_message("/wakeup", 'wakeup') 159 | self.send_mqtt_message("/seeebot", 'How can I help you?') 160 | self.tts.output_audio('How can I help you? ') 161 | break 162 | break 163 | 164 | if self.wakeup and self.asr_interrupt_flag[1] > 0.1: 165 | self.send_mqtt_message.send_msg("/human", self.asr_interrupt_flag[0]) 166 | output = self.agent.run(input=self.asr_interrupt_flag[0]) 167 | self.send_mqtt_message.send_msg("/seeebot", output) 168 | print(output) 169 | self.tts.output_audio(output) 170 | time.sleep(1) 171 | self.asr_interrupt_flag = None 172 | self.asr_mute = False 173 | else: 174 | time.sleep(0.1) 175 | 176 | def run_webui(self): 177 | import gradio as gr 178 | 179 | def respond(message): 180 | self.send_mqtt_message("/human", message) 181 | bot_message = self.agent.run(input=message) 182 | self.send_mqtt_message("/seeebot", bot_message) 183 | self.chat_history.append((message, bot_message)) 184 | return "", self.chat_history 185 | 186 | with gr.Blocks() as demo: 187 | gr.Markdown("# This is a simple webUI for reMirror") 188 | chat_msg = gr.Chatbot(height=500) 189 | msg = gr.Textbox(label="Prompt") 190 | btn = gr.Button("Submit") 191 | clear = gr.ClearButton(components=[msg, chat_msg], value="Clear console") 192 | btn.click(respond, inputs=[msg], outputs=[msg, chat_msg]) 193 | msg.submit(respond, inputs=[msg], outputs=[msg, chat_msg]) 194 | gr.close_all() 195 | demo.launch() 196 | 197 | def on_asr_transcript(self, response): 198 | try: 199 | if self.asr_mute: 200 | # print('waiting tts...') 201 | return 'waiting...' 202 | for result in response.results: 203 | if self.asr_mute: 204 | return 'waiting...' 205 | if not result.alternatives: 206 | continue 207 | transcript = result.alternatives[0].transcript 208 | if result.is_final: 209 | if 'How can' in transcript or 'how can' in transcript: 210 | return 'wakeup...' 211 | self.asr_interrupt_flag = (transcript, result.alternatives[0].confidence) 212 | print("## " + transcript + f"Confidence:{result.alternatives[0].confidence:9.4f}") 213 | else: 214 | # print('>> ' + transcript) 215 | pass 216 | finally: 217 | pass 218 | 219 | def send_mqtt_message(self, topic, message): 220 | if self.mqtt_client is not None: 221 | self.mqtt_client.send_msg(topic, message) 222 | else: 223 | return 0 224 | 225 | @staticmethod 226 | def get_riva_devices_list(): 227 | input_device, output_device = None, None 228 | p = pyaudio.PyAudio() 229 | 230 | print("Input audio devices:") 231 | for i in range(p.get_device_count()): 232 | info = p.get_device_info_by_index(i) 233 | if info['maxInputChannels'] < 1: 234 | continue 235 | if 'USB' in info["name"]: 236 | input_device = info 237 | print(f"{info['index']}: {info['name']}") 238 | 239 | print("Output audio devices:") 240 | for i in range(p.get_device_count()): 241 | info = p.get_device_info_by_index(i) 242 | if info['maxOutputChannels'] < 1: 243 | continue 244 | if 'USB' in info["name"]: 245 | output_device = info 246 | print(f"{info['index']}: {info['name']}") 247 | 248 | p.terminate() 249 | if input_device is None or output_device is None: 250 | print('No available riva device found, please manually config an device.') 251 | sys.exit(0) 252 | else: 253 | return input_device, output_device 254 | 255 | 256 | if __name__ == '__main__': 257 | args = parse_args() 258 | chatbot = SeeeBot( 259 | openai_api=args.openai_api, 260 | text_inference_server=args.text_inference_server, 261 | riva_server=args.riva_server, 262 | ha_access_token=args.ha_access_token 263 | ) 264 | chatbot.run() 265 | 266 | -------------------------------------------------------------------------------- /Software/llm/ha.py: -------------------------------------------------------------------------------- 1 | from requests import get, post 2 | import sys 3 | 4 | 5 | class HomeAssistant: 6 | 7 | def __init__(self, ha_access_token, ha_base_url): 8 | 9 | self.access_token = ha_access_token 10 | self.base_url = ha_base_url # "http://aeassistant.lan:8123/" 11 | 12 | self.switch_entity_ids = { 13 | 'air_conditioner': 'switch.qdhkl_ac_0146_switch_status', 14 | } 15 | 16 | def setup_switch(self, state, _entity_id): 17 | url = self.base_url 18 | if state == 'on': 19 | url += 'api/services/switch/turn_on' 20 | elif state == 'off': 21 | url += 'api/services/switch/turn_off' 22 | else: 23 | print("ERROR: Switch state must be 'on' or 'off'.") 24 | sys.exit() 25 | headers = {"Authorization": 'Bearer ' + self.access_token} 26 | data = {"entity_id": _entity_id} 27 | 28 | response = post(url, headers=headers, json=data) 29 | return response 30 | 31 | def turn_on_air_conditioner(self, args): 32 | self.setup_switch('on', self.switch_entity_ids['air_conditioner']) 33 | return 'Frank have turned on the air conditioner for human.' 34 | 35 | def turn_off_air_conditioner(self, args): 36 | self.setup_switch('off', self.switch_entity_ids['air_conditioner']) 37 | return 'Frank have turned off the air conditioner for human.' 38 | 39 | 40 | if __name__ == '__main__': 41 | ha = HomeAssistant() 42 | ha.setup_switch('off', ha.entity_ids['light']) 43 | -------------------------------------------------------------------------------- /Software/llm/local_llm.py: -------------------------------------------------------------------------------- 1 | from typing import Any, List, Mapping, Optional 2 | from langchain.llms.base import LLM 3 | import requests 4 | 5 | 6 | class CustomLLM(LLM): 7 | url = 'http://192.168.49.103:8899/generate' 8 | 9 | # Activate logits sampling 10 | do_sample: bool = False 11 | # Maximum number of generated tokens 12 | max_new_tokens: int = 20 13 | # The parameter for repetition penalty. 1.0 means no penalty. 14 | # See [this paper](https://arxiv.org/pdf/1909.05858.pdf) for more details. 15 | repetition_penalty: Optional[float] = None 16 | # Whether to prepend the prompt to the generated text 17 | return_full_text: bool = False 18 | # Stop generating tokens if a member of `stop_sequences` is generated 19 | stop: List[str] = [] 20 | # Random sampling seed 21 | seed: Optional[int] = None 22 | # The value used to module the logits distribution. 23 | temperature: Optional[float] = None 24 | # The number of highest probability vocabulary tokens to keep for top-k-filtering. 25 | top_k: Optional[int] = None 26 | # If set to < 1, only the smallest set of most probable tokens with probabilities that add up to `top_p` or 27 | # higher are kept for generation. 28 | top_p: Optional[float] = None 29 | # truncate inputs tokens to the given size 30 | truncate: Optional[int] = None 31 | # Typical Decoding mass 32 | # See [Typical Decoding for Natural Language Generation](https://arxiv.org/abs/2202.00666) for more information 33 | typical_p: Optional[float] = None 34 | # Generate best_of sequences and return the one if the highest token logprobs 35 | best_of: Optional[int] = None 36 | # Watermarking with [A Watermark for Large Language Models](https://arxiv.org/abs/2301.10226) 37 | watermark: bool = False 38 | # Get generation details 39 | details: bool = False 40 | # Get decoder input token logprobs and ids 41 | decoder_input_details: bool = False 42 | # Return the N most likely tokens at each step 43 | top_n_tokens: Optional[int] = None 44 | 45 | @property 46 | def _llm_type(self) -> str: 47 | return "custom" 48 | 49 | def _construct_query(self, prompt: str) -> List: 50 | headers = {"Content-Type": "application/json", } 51 | data = { 52 | 'inputs': prompt, 53 | 'parameters': { 54 | 'do_sample': self.do_sample, 55 | 'max_new_tokens': self.max_new_tokens, 56 | 'repetition_penalty': self.repetition_penalty, 57 | 'return_full_text': self.return_full_text, 58 | 'stop': self.stop, 59 | 'seed': self.seed, 60 | 'temperature': self.temperature, 61 | 'top_k': self.top_k, 62 | 'top_p': self.top_p, 63 | 'truncate': self.truncate, 64 | 'typical_p': self.typical_p, 65 | 'best_of': self.best_of, 66 | 'watermark': self.watermark, 67 | 'details': self.details, 68 | 'decoder_input_details': self.decoder_input_details, 69 | 'top_n_tokens': self.top_n_tokens 70 | }, 71 | } 72 | return [headers, data] 73 | 74 | def _call(self, prompt: str, **kwargs) -> str: 75 | 76 | # construct query 77 | query = self._construct_query(prompt=prompt) 78 | 79 | # post 80 | resp = requests.post(self.url, headers=query[0], json=query[1]) 81 | 82 | if resp.status_code == 200: 83 | resp_json = resp.json() 84 | predictions = resp_json['generated_text'] 85 | # print(f">>>>{predictions}<<<<<<") 86 | return predictions 87 | else: 88 | return "waiting..." 89 | 90 | 91 | @property 92 | def _identifying_params(self) -> Mapping[str, Any]: 93 | return {"url": self.url} 94 | 95 | 96 | if __name__ == '__main__': 97 | from langchain.chains.conversation.memory import ConversationBufferWindowMemory 98 | from langchain.chains import ConversationChain 99 | from langchain_core.prompts.prompt import PromptTemplate 100 | 101 | llm = CustomLLM(url='http://192.168.49.103:8899/generate', temperature=0.5, max_new_tokens=100) 102 | memory = ConversationBufferWindowMemory(k=5) 103 | 104 | template = """ Please answer the human question in short sentences. Just answer the questions and don't generate too much content. 105 | Current conversation: 106 | {history} 107 | Human: {input} 108 | AI: 109 | """ 110 | PROMPT = PromptTemplate(input_variables=["history", "input"], template=template) 111 | conversation = ConversationChain(llm=llm, verbose=True, prompt=PROMPT, memory=memory) 112 | output = conversation.predict(input="How many countries are there in the world") 113 | print(output) 114 | output = conversation.predict(input="It is good") 115 | print(output) 116 | # headers = {"Content-Type": "application/json", } 117 | # data = { 118 | # 'inputs': 'How many countries are there in the world', 119 | # 'parameters': { 120 | # 'max_new_tokens': 100, 121 | # 'temperature': 0.5, 122 | # }, 123 | # } 124 | # resp = requests.post('http://192.168.49.103:8899/generate', headers=headers, json=data) 125 | # 126 | # if resp.status_code == 200: 127 | # resp_json = resp.json() 128 | # predictions = resp_json['generated_text'] 129 | # print(predictions) 130 | # else: 131 | # print("waiting...") 132 | -------------------------------------------------------------------------------- /Software/llm/mqtt.py: -------------------------------------------------------------------------------- 1 | import time 2 | import paho.mqtt.client as mqtt 3 | 4 | 5 | class MQTT: 6 | def __init__(self, mqtt_broker="192.168.49.74", mqtt_port=1883): 7 | self.mqtt_broker = mqtt_broker 8 | self.mqtt_port = mqtt_port 9 | 10 | self.client = mqtt.Client() 11 | self.client.on_connect = self.on_connect 12 | self.client.connect(mqtt_broker, mqtt_port) 13 | self.client.loop_start() 14 | 15 | def send_msg(self, topic, message): 16 | try: 17 | self.client.publish(topic, message) 18 | except: 19 | pass 20 | 21 | @staticmethod 22 | def on_connect(client, userdata, flags, rc): 23 | if rc == 0: 24 | print("Connected to MQTT Broker!") 25 | else: 26 | print(f"Failed to connect, return code {rc}\n") 27 | 28 | def release(self): 29 | self.client.loop_stop() 30 | self.client.disconnect() 31 | 32 | 33 | if __name__ == '__main__': 34 | mqtt_client = MQTT(mqtt_broker="192.168.49.74", mqtt_port=1883) 35 | mqtt_client.send_msg("/seeebot", "service_enabled_nlp=false The models are installed and configured if they are uncommented in") 36 | time.sleep(1) 37 | # while True: 38 | # mqtt_client.send_msg("/seeebot", "Hello") 39 | # time.sleep(2) 40 | 41 | -------------------------------------------------------------------------------- /Software/llm/readme.md: -------------------------------------------------------------------------------- 1 | # LLM for LocalJARVIS 2 | This is a very interesting project. We connect large language models with Home Assistant through Langchain, using the 3 | large language models to control smart furniture. 4 | 5 | ## Hardware Connection 6 | 7 | - [reComputer](https://www.seeedstudio.com/reComputer-Industrial-J4012-p-5684.html) 8 | - [reSpeaker](https://www.seeedstudio.com/ReSpeaker-USB-Mic-Array-p-4247.html?queryID=ce3473db56fa363c3ddbf45d9f465548&objectID=4247&indexName=bazaar_retailer_products) 9 | - [Home Assistant Green](https://www.seeedstudio.com/Home-Assistant-Green-p-5792.html?queryID=2a68661c2c161dffa1ea6547eaf78474&objectID=5792&indexName=bazaar_retailer_products) 10 | - [Display](https://www.seeedstudio.com/15-6-Inch-IPS-Portable-Monitor-p-5757.html?queryID=e09b53d6697c11023e91b14fe847b7e0&objectID=5757&indexName=bazaar_retailer_products) 11 | 12 | **(Need a picture hardware connection)** 13 | 14 | ## LLM Installation 15 | 16 | ### Basic Dependencies 17 | Use following command to install the basic dependencies: 18 | ```shell 19 | pip3 install -r --no-cache-dir --verbose requirement.txt 20 | ``` 21 | ### Riva 22 | 23 | Refer to [this wiki](https://wiki.seeedstudio.com/Local_Voice_Chatbot/#install-riva-server) to install Riva. 24 | 25 | ### Local LLM 26 | 27 | To simplify the installation process, we can refer to Dusty's [jetson-containers](https://github.com/dusty-nv/jetson-containers/tree/master/packages/llm/text-generation-inference) project to install text generation inference, and use text generation inference to load the [Llama2-7B](https://huggingface.co/meta-llama) large language model. 28 | 29 | Open a new terminal on reComputer and run the following command. 30 | ```shell 31 | cd ~ 32 | git clone https://github.com/dusty-nv/jetson-containers.git 33 | cd jetson-containers 34 | pip install -r requirements.txt 35 | ./run.sh $(./autotag text-generation-inference) 36 | export HUGGING_FACE_HUB_TOKEN= 37 | text-generation-launcher --model-id meta-llama/Llama-2-7b-chat-hf --port 8899 38 | ``` 39 | ![avatar](./sources/text-generation-inference.png) 40 | You can obtain the hugging face token [here](https://huggingface.co/docs/hub/security-tokens). 41 | Please keep this terminal alive. 42 | 43 | ## Let's run it 44 | 45 | ### 1. The basic version ( WebUI+OpenAI ) 46 | 47 | ```shell 48 | python3 chatbot.py \ 49 | --openai_api \ 50 | --ha_access_token \ 51 | --ha_base_url 52 | ``` 53 | 54 | ### 2. Use Riva ASR/TTS Server 55 | **Please make sure you have installed the Riva.** 56 | 57 | ```shell 58 | python3 chatbot.py \ 59 | --openai_api \ 60 | --riva_server \ 61 | --ha_access_token \ 62 | --ha_base_url 63 | ``` 64 | 65 | ### 3. Use local LLM 66 | 67 | **Please make sure you have installed the local model.** 68 | 69 | You must set `openai_api` to `None` and set `text_inference_server` correctly. 70 | Please change the IP of the reComputer device in the following example. 71 | 72 | ```shell 73 | python3 chatbot_v2.1.py --text_inference_server http://192.168.49.104:8899/generate 74 | ``` 75 | 76 | ```shell 77 | python3 chatbot.py \ 78 | --text_inference_server http://:/generate \ 79 | --ha_access_token \ 80 | --ha_base_url 81 | ``` 82 | 83 | ## Run results 84 | https://youtu.be/7t7UspjepdE?si=A8KTiYOZwjy5Ur5C 85 | 86 | https://youtu.be/6fRZiXK9qT0?si=qdh0ijOq3WGenboA -------------------------------------------------------------------------------- /Software/llm/sources/text-generation-inference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Software/llm/sources/text-generation-inference.png -------------------------------------------------------------------------------- /Software/llm/tts.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import time 3 | import riva.client 4 | import riva.client.audio_io 5 | 6 | 7 | class TTS: 8 | def __init__(self, auth, output_device=None, sample_rate_hz=16000, language_code='en-US', voice='English-US.Female-1'): 9 | self.tts_service = riva.client.SpeechSynthesisService(auth) 10 | self.output_device = output_device 11 | self.sample_rate_hz = sample_rate_hz 12 | self.language_code = language_code 13 | self.voice = voice 14 | 15 | def output_audio(self, text): 16 | sound_stream = None 17 | try: 18 | sound_stream = riva.client.audio_io.SoundCallBack( 19 | self.output_device, nchannels=1, sampwidth=2, framerate=self.sample_rate_hz 20 | ) 21 | 22 | responses = self.tts_service.synthesize_online( 23 | text, self.voice, self.language_code, sample_rate_hz=self.sample_rate_hz 24 | ) 25 | 26 | for resp in responses: 27 | if sound_stream is not None: 28 | sound_stream(resp.audio) 29 | finally: 30 | if sound_stream is not None: 31 | sound_stream.close() 32 | 33 | @staticmethod 34 | def list_devices(): 35 | riva.client.audio_io.list_output_devices() 36 | 37 | 38 | if __name__ == '__main__': 39 | TTS.list_devices() 40 | 41 | # riva_server = "192.168.49.103:50051" 42 | # output_device = 7 43 | # sample_rate_hz = 48000 44 | # auth = riva.client.Auth(uri=riva_server) 45 | # tts = TTS(auth, output_device, sample_rate_hz) 46 | # tts.output_audio( 47 | # "According to the test standards you provided, under the premise that the resource utilization rate " 48 | # "should not exceed 70%, the reComputer can support up to 8 cameras." 49 | # ) 50 | # print('Done') 51 | # time.sleep(2) 52 | -------------------------------------------------------------------------------- /Software/node-red/README.md: -------------------------------------------------------------------------------- 1 | # Magic Mirror Dashboard Setup with Node-RED 🪄🖥️ 2 | 3 | This guide leads you through creating a dynamic Magic Mirror Dashboard using Node-RED on your Jetson device. It details steps for setting up the dashboard's main interface and integrating advanced features with a Grove Vision AI V2 Camera. 4 | 5 | ## What's Inside 📦 6 | 7 | - **`main_flow.json`**: Acts as the foundation of the Magic Mirror Dashboard setup. This file outlines the primary features and appearance of the dashboard. 8 | 9 | - **`grove_vision_ai_flows.json`**: Augments your dashboard by incorporating an overlay for the Grove Vision AI V2 Camera, facilitating the counting of people and monitoring of area zones. 10 | 11 | ### Dashboard Preview 🖼️ 12 | 13 | - **Magic Mirror Dashboard**: A sleek and interactive interface for your mirror. 14 | - **Grove Vision AI Overlay**: Introduces intelligent vision capabilities to your dashboard. 15 | 16 | --- 17 | 18 | ## Getting Started on Jetson 🚀 19 | 20 | Follow these instructions to install Node-RED and configure the necessary environment for your Magic Mirror Dashboard. 21 | 22 | ### 1. Install Node-RED 🧰 23 | 24 | **Create a dedicated folder for Node-RED data:** 25 | 26 | ```bash 27 | mkdir /home/nvidia/node_red_data/ 28 | ``` 29 | 30 | **Adjust the folder's ownership:** 31 | 32 | ```bash 33 | sudo chown 1000 /home/nvidia/node_red_data/ 34 | ``` 35 | 36 | **Launch Node-RED using Docker, naming the container "localJarvis-dashboard":** 37 | 38 | ```bash 39 | docker run --restart always -d -p 1880:1880 -v /home/nvidia/node_red_data:/data --name localJarvis-dashboard nodered/node-red:3.1 40 | ``` 41 | 42 | ### 2. Host Static Data 📂 43 | 44 | **Establish a directory for static data:** 45 | 46 | ```bash 47 | mkdir node-red-static 48 | ``` 49 | 50 | **Modify `settings.js` to utilize the new static data folder:** 51 | 52 | ```bash 53 | sed -i "s|//httpStatic: '/home/nol/node-red-static/'|httpStatic: '/data/node-red-static/'|g" settings.js 54 | ``` 55 | 56 | ### 3. Install Node-RED Package Dependency 📌 57 | 58 | **Access the Node-RED editor by navigating to your Jetson's IP address with port 1880.** 59 | 60 | To install Node-RED dependencies, enter the manage palette settings panel: 61 | 62 |

63 | 64 |

65 | 66 | Then, select "install": 67 | 68 |

69 | 70 |

71 | 72 | **In the search bar, install the following packages:** 73 | 74 | - node-red-dashboard 75 |

76 | 77 |

78 | - node-red-contrib-home-assistant-websocket 79 |

80 | 81 |

82 | 83 | ### 4. Import Node-RED Flow 🔄 84 | 85 | To import the flow, please follow the steps shown below: 86 | 87 |

88 | 89 |

90 | 91 |

92 | 93 |

94 | 95 |

96 | 97 |

98 | 99 | Finally, click "Deploy" to apply your changes. 100 | 101 |

102 | 103 |

104 | 105 | ### 5. Dashboard Settings (Work in Progress) 🔧 106 | 107 | - **Home Assistant Server address**: Specify your Home Assistant server address; for example: 192.168.100.100:8123 108 | - **MQTT server address and topics**: Provide your MQTT broker address; for example: 192.168.100.100:1883 109 | 110 | **MQTT Topics:** 111 | 112 | - `/human` "This is the TTS from human user voice input" 113 | - `/seeebot` "This is the AI response" 114 | - `/wakeup` "This signals the wakeup word." 115 | 116 | --- 117 | 118 | To access your dashboard, open a web browser and navigate to the IP address of your Jetson device, appending port 1880 followed by "/ui". 119 | 120 | The complete URL will appear as follows: "ip-address:1880/ui". For instance, in my scenario, it's "192.168.100.100:1880/ui". 121 | 122 |

123 | 124 |

125 | 126 | Congratulations! 🎉 You are now ready to delve into and personalize your Magic Mirror Dashboard. Don't hesitate to try out different Node-RED flows to introduce new functionalities or refine the current setup. Enjoy your creative journey! -------------------------------------------------------------------------------- /Software/node-red/grove_vision_ai_flows.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "a16955f928abc4fe", 4 | "type": "tab", 5 | "label": "Flow 2", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "e5ec67a5da37d2ba", 11 | "type": "mqtt in", 12 | "z": "a16955f928abc4fe", 13 | "name": "sub", 14 | "topic": "", 15 | "qos": "0", 16 | "datatype": "auto-detect", 17 | "broker": "8441d4244b560164", 18 | "nl": false, 19 | "rap": true, 20 | "rh": 0, 21 | "inputs": 1, 22 | "x": 930, 23 | "y": 400, 24 | "wires": [ 25 | [ 26 | "01eaf629f220d8b2", 27 | "8ea177454250ee7a" 28 | ] 29 | ] 30 | }, 31 | { 32 | "id": "5b27adc815dc6f68", 33 | "type": "debug", 34 | "z": "a16955f928abc4fe", 35 | "name": "payload", 36 | "active": false, 37 | "tosidebar": true, 38 | "console": false, 39 | "tostatus": false, 40 | "complete": "payload", 41 | "targetType": "msg", 42 | "statusVal": "", 43 | "statusType": "auto", 44 | "x": 1540, 45 | "y": 80, 46 | "wires": [] 47 | }, 48 | { 49 | "id": "f2dd7966cf0406d2", 50 | "type": "http request", 51 | "z": "a16955f928abc4fe", 52 | "name": "POST", 53 | "method": "POST", 54 | "ret": "txt", 55 | "paytoqs": "ignore", 56 | "url": "http://192.168.49.20:9081/72d6be50", 57 | "tls": "", 58 | "persist": true, 59 | "proxy": "", 60 | "insecureHTTPParser": false, 61 | "authType": "", 62 | "senderr": false, 63 | "headers": [ 64 | { 65 | "keyType": "Content-Type", 66 | "keyValue": "", 67 | "valueType": "other", 68 | "valueValue": "application/json" 69 | } 70 | ], 71 | "x": 1260, 72 | "y": 620, 73 | "wires": [ 74 | [ 75 | "4b19a8b093d5e7f9", 76 | "2c4b34705396492f" 77 | ] 78 | ] 79 | }, 80 | { 81 | "id": "4b19a8b093d5e7f9", 82 | "type": "json", 83 | "z": "a16955f928abc4fe", 84 | "name": "", 85 | "property": "payload", 86 | "action": "", 87 | "pretty": false, 88 | "x": 1410, 89 | "y": 620, 90 | "wires": [ 91 | [ 92 | "bebebee085d93c3b", 93 | "bd752c0c2256f23e", 94 | "a0863aa0e9fa6a3a" 95 | ] 96 | ] 97 | }, 98 | { 99 | "id": "d18eb91e78e9d36d", 100 | "type": "debug", 101 | "z": "a16955f928abc4fe", 102 | "d": true, 103 | "name": "raw to tracker", 104 | "active": true, 105 | "tosidebar": true, 106 | "console": false, 107 | "tostatus": false, 108 | "complete": "payload", 109 | "targetType": "msg", 110 | "statusVal": "", 111 | "statusType": "auto", 112 | "x": 1490, 113 | "y": 520, 114 | "wires": [] 115 | }, 116 | { 117 | "id": "bebebee085d93c3b", 118 | "type": "function", 119 | "z": "a16955f928abc4fe", 120 | "name": "postPost", 121 | "func": "if (msg.payload.annotations) {\n msg.payload = msg.payload.annotations[0];\n msg.topic = \"track\"\n}\n\nreturn msg;", 122 | "outputs": 1, 123 | "timeout": 0, 124 | "noerr": 0, 125 | "initialize": "", 126 | "finalize": "", 127 | "libs": [], 128 | "x": 1590, 129 | "y": 580, 130 | "wires": [ 131 | [ 132 | "2005160d7ce952b6", 133 | "16763f0516016cec" 134 | ] 135 | ] 136 | }, 137 | { 138 | "id": "969732a895833caf", 139 | "type": "mqtt out", 140 | "z": "a16955f928abc4fe", 141 | "name": "pub", 142 | "topic": "", 143 | "qos": "0", 144 | "retain": "false", 145 | "respTopic": "", 146 | "contentType": "", 147 | "userProps": "", 148 | "correl": "", 149 | "expiry": "", 150 | "broker": "8441d4244b560164", 151 | "x": 970, 152 | "y": 800, 153 | "wires": [] 154 | }, 155 | { 156 | "id": "8ea177454250ee7a", 157 | "type": "function", 158 | "z": "a16955f928abc4fe", 159 | "name": "pre", 160 | "func": "\n// if (msg.payload.data.boxes && \n// msg.payload.data.boxes.length > 0) \nif (msg.payload.data)\n{\n let annotations_list = [];\n if (flow.get(\"polygon\")) \n annotations_list.push(\"polygon\");\n if (flow.get(\"heatmap\")) \n annotations_list.push(\"heatmap\");\n if (flow.get(\"bounding_box\")) \n annotations_list.push(\"bounding_box\");\n if (flow.get(\"tracing\")) \n annotations_list.push(\"tracing\");\n if (flow.get(\"labeling\")) \n annotations_list.push(\"labeling\");\n \n msg.payload.data.annotations = [];\n msg.payload.data.annotations.push(annotations_list);\n\n msg.payload = JSON.stringify(msg.payload.data);\n}\nelse \n msg = null;\n\nreturn msg;", 161 | "outputs": 1, 162 | "timeout": 0, 163 | "noerr": 0, 164 | "initialize": "// 部署节点后,此处添加的代码将运行一次。 \n// flow.set(\"polygon\", false);\n// flow.set(\"bounding_box\", false);\n// flow.set(\"tracing\", false);\n// flow.set(\"labeling\", false);\n// flow.set(\"heatmap\", false);", 165 | "finalize": "", 166 | "libs": [], 167 | "x": 1110, 168 | "y": 580, 169 | "wires": [ 170 | [ 171 | "f2dd7966cf0406d2", 172 | "8e0e42681fc48b98" 173 | ] 174 | ] 175 | }, 176 | { 177 | "id": "e7dad17999087f9c", 178 | "type": "http request", 179 | "z": "a16955f928abc4fe", 180 | "name": "POST", 181 | "method": "POST", 182 | "ret": "txt", 183 | "paytoqs": "ignore", 184 | "url": "http://192.168.49.20:9081", 185 | "tls": "", 186 | "persist": true, 187 | "proxy": "", 188 | "insecureHTTPParser": false, 189 | "authType": "", 190 | "senderr": false, 191 | "headers": [ 192 | { 193 | "keyType": "Content-Type", 194 | "keyValue": "", 195 | "valueType": "application/json", 196 | "valueValue": "" 197 | }, 198 | { 199 | "keyType": "other", 200 | "keyValue": "Session-Id", 201 | "valueType": "other", 202 | "valueValue": "72d6be50" 203 | } 204 | ], 205 | "x": 1180, 206 | "y": 1000, 207 | "wires": [ 208 | [ 209 | "2fe5c3afc703932a" 210 | ] 211 | ] 212 | }, 213 | { 214 | "id": "0ca99a1e20c907f5", 215 | "type": "debug", 216 | "z": "a16955f928abc4fe", 217 | "name": "result", 218 | "active": false, 219 | "tosidebar": true, 220 | "console": false, 221 | "tostatus": false, 222 | "complete": "payload", 223 | "targetType": "msg", 224 | "statusVal": "", 225 | "statusType": "auto", 226 | "x": 1180, 227 | "y": 940, 228 | "wires": [] 229 | }, 230 | { 231 | "id": "01eaf629f220d8b2", 232 | "type": "switch", 233 | "z": "a16955f928abc4fe", 234 | "name": "", 235 | "property": "payload.data.image", 236 | "propertyType": "msg", 237 | "rules": [ 238 | { 239 | "t": "nempty" 240 | } 241 | ], 242 | "checkall": "true", 243 | "repair": false, 244 | "outputs": 1, 245 | "x": 1120, 246 | "y": 260, 247 | "wires": [ 248 | [ 249 | "376dc2e1553a4a42" 250 | ] 251 | ] 252 | }, 253 | { 254 | "id": "376dc2e1553a4a42", 255 | "type": "change", 256 | "z": "a16955f928abc4fe", 257 | "name": "get image", 258 | "rules": [ 259 | { 260 | "t": "set", 261 | "p": "payload", 262 | "pt": "msg", 263 | "to": "payload.data.image", 264 | "tot": "msg" 265 | }, 266 | { 267 | "t": "set", 268 | "p": "topic", 269 | "pt": "msg", 270 | "to": "raw", 271 | "tot": "str" 272 | } 273 | ], 274 | "action": "", 275 | "property": "", 276 | "from": "", 277 | "to": "", 278 | "reg": false, 279 | "x": 1350, 280 | "y": 200, 281 | "wires": [ 282 | [ 283 | "72d9c00611084f47", 284 | "5b27adc815dc6f68", 285 | "50cfad0ab7491acf" 286 | ] 287 | ] 288 | }, 289 | { 290 | "id": "2fe5c3afc703932a", 291 | "type": "function", 292 | "z": "a16955f928abc4fe", 293 | "name": "msg", 294 | "func": "msg.payload = msg\nreturn msg;", 295 | "outputs": 1, 296 | "timeout": 0, 297 | "noerr": 0, 298 | "initialize": "", 299 | "finalize": "", 300 | "libs": [], 301 | "x": 1330, 302 | "y": 1000, 303 | "wires": [ 304 | [] 305 | ] 306 | }, 307 | { 308 | "id": "9a1990c75bf6f8f8", 309 | "type": "ui_button", 310 | "z": "a16955f928abc4fe", 311 | "name": "session", 312 | "group": "9530089c40af5601", 313 | "order": 2, 314 | "width": "3", 315 | "height": "1", 316 | "passthru": false, 317 | "label": "refresh", 318 | "tooltip": "", 319 | "color": "", 320 | "bgcolor": "", 321 | "className": "to_center", 322 | "icon": "", 323 | "payload": "{\"tracker_config\":{\"track_thresh\":0.25,\"track_buffer\":30,\"match_thresh\":0.8,\"frame_rate\":30},\"annotation_config\":{\"resolution\":[240,240],\"polygon\":{\"thickness\":1,\"text_scale\":0.3,\"text_thickness\":0.5,\"text_padding\":2},\"bounding_box\":{\"thickness\":2},\"tracing\":{\"position\":\"CENTER\",\"trace_length\":30,\"trace_thickness\":1},\"labeling\":{\"text_scale\":0.3,\"text_thickness\":1,\"text_padding\":3,\"text_position\":\"TOP_LEFT\",\"label_map\":{\"0\":\"Person\",\"1\":\"Car\",\"2\":\"Truck\"}},\"heatmap\":{\"position\":\"BOTTOM_CENTER\",\"opacity\":0.25,\"radius\":10,\"kernel_size\":5}},\"regions_config\":{\"Region_A\":{\"polygon\":[[0,0],[240,0],[240,160],[0,80]],\"triggering_position\":\"CENTER\"},\"Region_B\":{\"polygon\":[[0,80],[240,160],[240,240],[0,240]],\"triggering_position\":\"CENTER\"}}}", 324 | "payloadType": "json", 325 | "topic": "track_session", 326 | "topicType": "str", 327 | "x": 840, 328 | "y": 1000, 329 | "wires": [ 330 | [ 331 | "335acff635759144" 332 | ] 333 | ] 334 | }, 335 | { 336 | "id": "436c459aa7730fbf", 337 | "type": "ui_switch", 338 | "z": "a16955f928abc4fe", 339 | "name": "invoke", 340 | "label": "invoke", 341 | "tooltip": "", 342 | "group": "9530089c40af5601", 343 | "order": 3, 344 | "width": "3", 345 | "height": "1", 346 | "passthru": false, 347 | "decouple": "false", 348 | "topic": "mqtt_rx", 349 | "topicType": "flow", 350 | "style": "", 351 | "onvalue": "[65,84,43,73,78,86,79,75,69,61,45,49,44,48,44,48,13,10]", 352 | "onvalueType": "bin", 353 | "onicon": "", 354 | "oncolor": "", 355 | "offvalue": "[65,84,43,66,82,69,65,75,13,10]", 356 | "offvalueType": "bin", 357 | "officon": "", 358 | "offcolor": "", 359 | "animate": false, 360 | "className": "", 361 | "x": 760, 362 | "y": 800, 363 | "wires": [ 364 | [ 365 | "969732a895833caf", 366 | "a9ec083431499714" 367 | ] 368 | ] 369 | }, 370 | { 371 | "id": "63993402cb62511d", 372 | "type": "ui_text_input", 373 | "z": "a16955f928abc4fe", 374 | "name": "dest", 375 | "label": "", 376 | "tooltip": "", 377 | "group": "9530089c40af5601", 378 | "order": 1, 379 | "width": 0, 380 | "height": 0, 381 | "passthru": true, 382 | "mode": "text", 383 | "delay": "0", 384 | "topic": "mqtt_dest", 385 | "sendOnBlur": true, 386 | "className": "", 387 | "topicType": "str", 388 | "x": 430, 389 | "y": 400, 390 | "wires": [ 391 | [ 392 | "6e4f19d7de1e5c80", 393 | "9e3a35e270eb5d14" 394 | ] 395 | ] 396 | }, 397 | { 398 | "id": "44b5504a21cb1bb0", 399 | "type": "change", 400 | "z": "a16955f928abc4fe", 401 | "name": "setup", 402 | "rules": [ 403 | { 404 | "t": "set", 405 | "p": "mqtt_tx", 406 | "pt": "flow", 407 | "to": "\"sscma/v0/\" & payload & \"/tx\"", 408 | "tot": "jsonata" 409 | }, 410 | { 411 | "t": "set", 412 | "p": "mqtt_rx", 413 | "pt": "flow", 414 | "to": "\"sscma/v0/\" & payload & \"/rx\"", 415 | "tot": "jsonata" 416 | }, 417 | { 418 | "t": "set", 419 | "p": "topic", 420 | "pt": "msg", 421 | "to": "\"sscma/v0/\" & payload & \"/tx\"", 422 | "tot": "jsonata" 423 | }, 424 | { 425 | "t": "set", 426 | "p": "action", 427 | "pt": "msg", 428 | "to": "subscribe", 429 | "tot": "str" 430 | }, 431 | { 432 | "t": "set", 433 | "p": "qos", 434 | "pt": "msg", 435 | "to": "0", 436 | "tot": "num" 437 | } 438 | ], 439 | "action": "", 440 | "property": "", 441 | "from": "", 442 | "to": "", 443 | "reg": false, 444 | "x": 780, 445 | "y": 460, 446 | "wires": [ 447 | [ 448 | "e5ec67a5da37d2ba" 449 | ] 450 | ] 451 | }, 452 | { 453 | "id": "5df7497493e3c0ed", 454 | "type": "inject", 455 | "z": "a16955f928abc4fe", 456 | "name": "default", 457 | "props": [ 458 | { 459 | "p": "payload" 460 | } 461 | ], 462 | "repeat": "", 463 | "crontab": "", 464 | "once": true, 465 | "onceDelay": "0.1", 466 | "topic": "", 467 | "payload": "grove_vision_ai_v2_cdbef6de", 468 | "payloadType": "str", 469 | "x": 600, 470 | "y": 520, 471 | "wires": [ 472 | [ 473 | "44b5504a21cb1bb0" 474 | ] 475 | ] 476 | }, 477 | { 478 | "id": "50cfad0ab7491acf", 479 | "type": "ui_template", 480 | "z": "a16955f928abc4fe", 481 | "group": "b8ef5709b7b160fb", 482 | "name": "original", 483 | "order": 10, 484 | "width": "5", 485 | "height": "5", 486 | "format": "\n
\n \n
\n\n\n", 487 | "storeOutMessages": false, 488 | "fwdInMessages": false, 489 | "resendOnRefresh": false, 490 | "templateScope": "local", 491 | "className": ".myClass", 492 | "x": 1620, 493 | "y": 280, 494 | "wires": [ 495 | [] 496 | ] 497 | }, 498 | { 499 | "id": "38db7efa2f522462", 500 | "type": "ui_gauge", 501 | "z": "a16955f928abc4fe", 502 | "name": "", 503 | "group": "b8ef5709b7b160fb", 504 | "order": 4, 505 | "width": "3", 506 | "height": "3", 507 | "gtype": "gage", 508 | "title": "Image", 509 | "label": "fps", 510 | "format": "{{value}}", 511 | "min": 0, 512 | "max": "15", 513 | "colors": [ 514 | "#ca3838", 515 | "#e6e600", 516 | "#00b500" 517 | ], 518 | "seg1": "4", 519 | "seg2": "6", 520 | "diff": false, 521 | "className": "", 522 | "x": 1880, 523 | "y": 320, 524 | "wires": [] 525 | }, 526 | { 527 | "id": "982f024e9730de25", 528 | "type": "ui_gauge", 529 | "z": "a16955f928abc4fe", 530 | "name": "", 531 | "group": "b8ef5709b7b160fb", 532 | "order": 4, 533 | "width": "3", 534 | "height": "3", 535 | "gtype": "gage", 536 | "title": "Overlay", 537 | "label": "fps", 538 | "format": "{{value}}", 539 | "min": 0, 540 | "max": "15", 541 | "colors": [ 542 | "#ca3838", 543 | "#e6e600", 544 | "#00b500" 545 | ], 546 | "seg1": "4", 547 | "seg2": "6", 548 | "diff": false, 549 | "className": "", 550 | "x": 2000, 551 | "y": 460, 552 | "wires": [] 553 | }, 554 | { 555 | "id": "16763f0516016cec", 556 | "type": "ui_template", 557 | "z": "a16955f928abc4fe", 558 | "group": "587f3511e9470eab", 559 | "name": "processed", 560 | "order": 15, 561 | "width": 5, 562 | "height": 5, 563 | "format": "\n\n", 564 | "storeOutMessages": false, 565 | "fwdInMessages": false, 566 | "resendOnRefresh": false, 567 | "templateScope": "local", 568 | "className": "", 569 | "x": 1880, 570 | "y": 580, 571 | "wires": [ 572 | [] 573 | ] 574 | }, 575 | { 576 | "id": "bd752c0c2256f23e", 577 | "type": "function", 578 | "z": "a16955f928abc4fe", 579 | "name": " flow statistics", 580 | "func": "var list_a = flow.get(\"list_a\");\nvar list_b = flow.get(\"list_b\");\n\nif (msg.payload.filtered_regions)\n{\n var set_a_pre = new Set(flow.get(\"list_a\"));\n var set_b_pre = new Set(flow.get(\"list_b\"));\n var set_a = new Set(msg.payload.filtered_regions.Region_A);\n var set_b = new Set(msg.payload.filtered_regions.Region_B);\n\n var map_all = new Map();\n for (let id of [...set_a_pre, ...set_a_pre]) {\n if (!map_all.has(id)) {\n map_all.set(id, 0);\n }\n }\n for (let [id, misses] of map_all.entries()) {\n if (!set_a.has(id) && !set_b.has(id)) {\n misses += 1;\n map_all.set(id, misses);\n if (misses >= 10) {\n map_all.delete(id);\n if (set_a_pre.has(id)) set_a_pre.delete(id);\n if (set_b_pre.has(id)) set_b_pre.delete(id);\n }\n } else {\n map_all.set(id, 0); // appear, reset counter\n }\n }\n\n for (let id of set_a_pre) {\n if (!set_a.has(id) && set_b.has(id)) {\n set_a_pre.delete(id);\n flow.set(\"cnt_a2b\", flow.get(\"cnt_a2b\") + 1);\n node.log(flow.get(\"cnt_a2b\"));\n }\n }\n\n for (let id of set_b_pre) {\n if (!set_b.has(id) && set_a.has(id)) {\n set_b_pre.delete(id);\n flow.set(\"cnt_b2a\", flow.get(\"cnt_b2a\") + 1);\n node.log(flow.get(\"cnt_b2a\"));\n }\n }\n\n msg.payload.filtered_regions.Region_A.forEach(set_a_pre.add, set_a_pre); \n msg.payload.filtered_regions.Region_B.forEach(set_b_pre.add, set_b_pre); \n // set_a_pre.add(msg.payload.filtered_regions.Region_A.slice());\n // set_b_pre.add(msg.payload.filtered_regions.Region_B.slice());\n flow.set(\"list_a\", [...set_a_pre]);\n flow.set(\"list_b\", [...set_b_pre]);\n // flow.set(\"list_a\", msg.payload.filtered_regions.Region_A.slice());\n // flow.set(\"list_b\", msg.payload.filtered_regions.Region_B.slice());\n\n return [{ payload: flow.get(\"cnt_a2b\") }, { payload: flow.get(\"cnt_b2a\") }];\n}\nelse \n{\n return [null, null];\n}", 581 | "outputs": 2, 582 | "timeout": 0, 583 | "noerr": 0, 584 | "initialize": "// 部署节点后,此处添加的代码将运行一次。 \nflow.set(\"list_a\", []);\nflow.set(\"list_b\", []);\n\nflow.set(\"cnt_a2b\", 0);\nflow.set(\"cnt_b2a\", 0);\n", 585 | "finalize": "", 586 | "libs": [], 587 | "x": 1670, 588 | "y": 680, 589 | "wires": [ 590 | [ 591 | "8e5ad3892713c8e2" 592 | ], 593 | [ 594 | "e8b3e664d70bac19", 595 | "d3651831feb7cf37" 596 | ] 597 | ] 598 | }, 599 | { 600 | "id": "b6e3f0bca2143015", 601 | "type": "change", 602 | "z": "a16955f928abc4fe", 603 | "name": "clean", 604 | "rules": [ 605 | { 606 | "t": "set", 607 | "p": "cnt_a2b", 608 | "pt": "flow", 609 | "to": "0", 610 | "tot": "num" 611 | }, 612 | { 613 | "t": "set", 614 | "p": "cnt_b2a", 615 | "pt": "flow", 616 | "to": "0", 617 | "tot": "num" 618 | } 619 | ], 620 | "action": "", 621 | "property": "", 622 | "from": "", 623 | "to": "", 624 | "reg": false, 625 | "x": 1130, 626 | "y": 880, 627 | "wires": [ 628 | [] 629 | ] 630 | }, 631 | { 632 | "id": "94ffbd16350d4058", 633 | "type": "ui_switch", 634 | "z": "a16955f928abc4fe", 635 | "name": "track", 636 | "label": "track", 637 | "tooltip": "", 638 | "group": "9530089c40af5601", 639 | "order": 7, 640 | "width": 0, 641 | "height": 0, 642 | "passthru": true, 643 | "decouple": "false", 644 | "topic": "track", 645 | "topicType": "str", 646 | "style": "", 647 | "onvalue": "true", 648 | "onvalueType": "bool", 649 | "onicon": "", 650 | "oncolor": "", 651 | "offvalue": "false", 652 | "offvalueType": "bool", 653 | "officon": "", 654 | "offcolor": "", 655 | "animate": false, 656 | "className": "", 657 | "x": 530, 658 | "y": 880, 659 | "wires": [ 660 | [ 661 | "72ec40207214b487" 662 | ] 663 | ] 664 | }, 665 | { 666 | "id": "0fda9e50bd5f35c5", 667 | "type": "ui_switch", 668 | "z": "a16955f928abc4fe", 669 | "name": "box", 670 | "label": "box", 671 | "tooltip": "", 672 | "group": "9530089c40af5601", 673 | "order": 6, 674 | "width": 0, 675 | "height": 0, 676 | "passthru": true, 677 | "decouple": "false", 678 | "topic": "box", 679 | "topicType": "str", 680 | "style": "", 681 | "onvalue": "true", 682 | "onvalueType": "bool", 683 | "onicon": "", 684 | "oncolor": "", 685 | "offvalue": "false", 686 | "offvalueType": "bool", 687 | "officon": "", 688 | "offcolor": "", 689 | "animate": false, 690 | "className": "", 691 | "x": 530, 692 | "y": 800, 693 | "wires": [ 694 | [ 695 | "72ec40207214b487" 696 | ] 697 | ] 698 | }, 699 | { 700 | "id": "a9ec083431499714", 701 | "type": "switch", 702 | "z": "a16955f928abc4fe", 703 | "name": "check", 704 | "property": "$length(msg.payload.toString())", 705 | "propertyType": "jsonata", 706 | "rules": [ 707 | { 708 | "t": "lt", 709 | "v": "18", 710 | "vt": "num" 711 | } 712 | ], 713 | "checkall": "true", 714 | "repair": false, 715 | "outputs": 1, 716 | "x": 980, 717 | "y": 880, 718 | "wires": [ 719 | [ 720 | "b6e3f0bca2143015" 721 | ] 722 | ] 723 | }, 724 | { 725 | "id": "8e5ad3892713c8e2", 726 | "type": "ui_gauge", 727 | "z": "a16955f928abc4fe", 728 | "name": "", 729 | "group": "587f3511e9470eab", 730 | "order": 25, 731 | "width": 2, 732 | "height": 2, 733 | "gtype": "compass", 734 | "title": "( A to B)", 735 | "label": "persons", 736 | "format": "{{value}}", 737 | "min": 0, 738 | "max": 10, 739 | "colors": [ 740 | "#00b500", 741 | "#e6e600", 742 | "#ca3838" 743 | ], 744 | "seg1": "", 745 | "seg2": "", 746 | "diff": false, 747 | "className": "", 748 | "x": 1940, 749 | "y": 660, 750 | "wires": [] 751 | }, 752 | { 753 | "id": "a8a777f0617ca665", 754 | "type": "ui_template", 755 | "z": "a16955f928abc4fe", 756 | "group": "9530089c40af5601", 757 | "name": "to_center", 758 | "order": 13, 759 | "width": 0, 760 | "height": 0, 761 | "format": "", 762 | "storeOutMessages": true, 763 | "fwdInMessages": true, 764 | "resendOnRefresh": true, 765 | "templateScope": "global", 766 | "className": "", 767 | "x": 1610, 768 | "y": 1120, 769 | "wires": [ 770 | [] 771 | ] 772 | }, 773 | { 774 | "id": "031c852a3273fc84", 775 | "type": "ui_switch", 776 | "z": "a16955f928abc4fe", 777 | "name": "region", 778 | "label": "region", 779 | "tooltip": "", 780 | "group": "9530089c40af5601", 781 | "order": 5, 782 | "width": 0, 783 | "height": 0, 784 | "passthru": true, 785 | "decouple": "false", 786 | "topic": "region", 787 | "topicType": "str", 788 | "style": "", 789 | "onvalue": "true", 790 | "onvalueType": "bool", 791 | "onicon": "", 792 | "oncolor": "", 793 | "offvalue": "false", 794 | "offvalueType": "bool", 795 | "officon": "", 796 | "offcolor": "", 797 | "animate": false, 798 | "className": "", 799 | "x": 540, 800 | "y": 1080, 801 | "wires": [ 802 | [ 803 | "72ec40207214b487" 804 | ] 805 | ] 806 | }, 807 | { 808 | "id": "a568506cead91716", 809 | "type": "ui_switch", 810 | "z": "a16955f928abc4fe", 811 | "name": "label", 812 | "label": "label", 813 | "tooltip": "", 814 | "group": "9530089c40af5601", 815 | "order": 8, 816 | "width": 0, 817 | "height": 0, 818 | "passthru": true, 819 | "decouple": "false", 820 | "topic": "label", 821 | "topicType": "str", 822 | "style": "", 823 | "onvalue": "true", 824 | "onvalueType": "bool", 825 | "onicon": "", 826 | "oncolor": "", 827 | "offvalue": "false", 828 | "offvalueType": "bool", 829 | "officon": "", 830 | "offcolor": "", 831 | "animate": false, 832 | "className": "", 833 | "x": 530, 834 | "y": 980, 835 | "wires": [ 836 | [ 837 | "72ec40207214b487" 838 | ] 839 | ] 840 | }, 841 | { 842 | "id": "72ec40207214b487", 843 | "type": "function", 844 | "z": "a16955f928abc4fe", 845 | "name": "setting", 846 | "func": "\nswitch(msg.topic)\n{\n case \"box\":\n flow.set(\"bounding_box\", msg.payload);\n break;\n case \"region\":\n flow.set(\"polygon\", msg.payload);\n break;\n case \"track\":\n flow.set(\"tracing\", msg.payload);\n break;\n case \"label\":\n flow.set(\"labeling\", msg.payload);\n break;\n case \"heatmap\":\n flow.set(\"heatmap\", msg.payload);\n break;\n default:\n return msg;\n}\n\nreturn msg;", 847 | "outputs": 1, 848 | "timeout": 0, 849 | "noerr": 0, 850 | "initialize": "", 851 | "finalize": "", 852 | "libs": [], 853 | "x": 760, 854 | "y": 920, 855 | "wires": [ 856 | [] 857 | ] 858 | }, 859 | { 860 | "id": "cfe25b38dd9c2b35", 861 | "type": "ui_switch", 862 | "z": "a16955f928abc4fe", 863 | "name": "heatmap", 864 | "label": "heatmap", 865 | "tooltip": "", 866 | "group": "9530089c40af5601", 867 | "order": 9, 868 | "width": 0, 869 | "height": 0, 870 | "passthru": true, 871 | "decouple": "false", 872 | "topic": "heatmap", 873 | "topicType": "str", 874 | "style": "", 875 | "onvalue": "true", 876 | "onvalueType": "bool", 877 | "onicon": "", 878 | "oncolor": "", 879 | "offvalue": "false", 880 | "offvalueType": "bool", 881 | "officon": "", 882 | "offcolor": "", 883 | "animate": false, 884 | "className": "", 885 | "x": 550, 886 | "y": 1180, 887 | "wires": [ 888 | [ 889 | "72ec40207214b487" 890 | ] 891 | ] 892 | }, 893 | { 894 | "id": "8e0e42681fc48b98", 895 | "type": "json", 896 | "z": "a16955f928abc4fe", 897 | "d": true, 898 | "name": "", 899 | "property": "payload", 900 | "action": "", 901 | "pretty": false, 902 | "x": 1290, 903 | "y": 520, 904 | "wires": [ 905 | [ 906 | "d18eb91e78e9d36d" 907 | ] 908 | ] 909 | }, 910 | { 911 | "id": "007f4ad7089c22c8", 912 | "type": "ui_text", 913 | "z": "a16955f928abc4fe", 914 | "group": "9530089c40af5601", 915 | "order": 4, 916 | "width": "4", 917 | "height": "1", 918 | "name": "", 919 | "label": "[Overlay]", 920 | "format": "{{msg.payload}}", 921 | "layout": "row-spread", 922 | "className": "", 923 | "style": false, 924 | "font": "", 925 | "fontSize": 16, 926 | "color": "#000000", 927 | "x": 1430, 928 | "y": 1120, 929 | "wires": [] 930 | }, 931 | { 932 | "id": "67db0c4cf3a2be44", 933 | "type": "ui_template", 934 | "z": "a16955f928abc4fe", 935 | "d": true, 936 | "group": "d783cb980c3979fc", 937 | "name": "Node-Red Dashboard Custom CSS", 938 | "order": 1, 939 | "width": 0, 940 | "height": 0, 941 | "format": "", 942 | "storeOutMessages": true, 943 | "fwdInMessages": true, 944 | "resendOnRefresh": false, 945 | "templateScope": "global", 946 | "className": "", 947 | "x": 1510, 948 | "y": 1180, 949 | "wires": [ 950 | [] 951 | ] 952 | }, 953 | { 954 | "id": "e8b3e664d70bac19", 955 | "type": "ui_gauge", 956 | "z": "a16955f928abc4fe", 957 | "name": "", 958 | "group": "587f3511e9470eab", 959 | "order": 27, 960 | "width": 2, 961 | "height": 2, 962 | "gtype": "compass", 963 | "title": "( B to A)", 964 | "label": "persons", 965 | "format": "{{value}}", 966 | "min": 0, 967 | "max": 10, 968 | "colors": [ 969 | "#00b500", 970 | "#e6e600", 971 | "#ca3838" 972 | ], 973 | "seg1": "", 974 | "seg2": "", 975 | "diff": false, 976 | "className": "", 977 | "x": 1980, 978 | "y": 720, 979 | "wires": [] 980 | }, 981 | { 982 | "id": "338788c6045cabdb", 983 | "type": "debug", 984 | "z": "a16955f928abc4fe", 985 | "name": "debug", 986 | "active": false, 987 | "tosidebar": true, 988 | "console": false, 989 | "tostatus": false, 990 | "complete": "payload", 991 | "targetType": "msg", 992 | "statusVal": "", 993 | "statusType": "auto", 994 | "x": 2120, 995 | "y": 780, 996 | "wires": [] 997 | }, 998 | { 999 | "id": "6e4f19d7de1e5c80", 1000 | "type": "delay", 1001 | "z": "a16955f928abc4fe", 1002 | "name": "delay", 1003 | "pauseType": "delay", 1004 | "timeout": "200", 1005 | "timeoutUnits": "milliseconds", 1006 | "rate": "1", 1007 | "nbRateUnits": "1", 1008 | "rateUnits": "second", 1009 | "randomFirst": "1", 1010 | "randomLast": "5", 1011 | "randomUnits": "seconds", 1012 | "drop": false, 1013 | "allowrate": false, 1014 | "outputs": 1, 1015 | "x": 610, 1016 | "y": 460, 1017 | "wires": [ 1018 | [ 1019 | "44b5504a21cb1bb0" 1020 | ] 1021 | ] 1022 | }, 1023 | { 1024 | "id": "9e3a35e270eb5d14", 1025 | "type": "change", 1026 | "z": "a16955f928abc4fe", 1027 | "name": "setup", 1028 | "rules": [ 1029 | { 1030 | "t": "set", 1031 | "p": "topic", 1032 | "pt": "msg", 1033 | "to": "mqtt_tx", 1034 | "tot": "flow" 1035 | }, 1036 | { 1037 | "t": "set", 1038 | "p": "action", 1039 | "pt": "msg", 1040 | "to": "unsubscribe", 1041 | "tot": "str" 1042 | } 1043 | ], 1044 | "action": "", 1045 | "property": "", 1046 | "from": "", 1047 | "to": "", 1048 | "reg": false, 1049 | "x": 700, 1050 | "y": 400, 1051 | "wires": [ 1052 | [ 1053 | "e5ec67a5da37d2ba" 1054 | ] 1055 | ] 1056 | }, 1057 | { 1058 | "id": "d3651831feb7cf37", 1059 | "type": "change", 1060 | "z": "a16955f928abc4fe", 1061 | "name": "get", 1062 | "rules": [ 1063 | { 1064 | "t": "set", 1065 | "p": "payload", 1066 | "pt": "msg", 1067 | "to": "{}", 1068 | "tot": "json" 1069 | }, 1070 | { 1071 | "t": "set", 1072 | "p": "payload.box_a", 1073 | "pt": "msg", 1074 | "to": "list_a", 1075 | "tot": "flow" 1076 | }, 1077 | { 1078 | "t": "set", 1079 | "p": "payload.box_b", 1080 | "pt": "msg", 1081 | "to": "list_b", 1082 | "tot": "flow" 1083 | }, 1084 | { 1085 | "t": "set", 1086 | "p": "payload.cnt_a2b", 1087 | "pt": "msg", 1088 | "to": "cnt_a2b", 1089 | "tot": "flow" 1090 | }, 1091 | { 1092 | "t": "set", 1093 | "p": "payload.cnt_b2a", 1094 | "pt": "msg", 1095 | "to": "cnt_b2a", 1096 | "tot": "flow" 1097 | } 1098 | ], 1099 | "action": "", 1100 | "property": "", 1101 | "from": "", 1102 | "to": "", 1103 | "reg": false, 1104 | "x": 1910, 1105 | "y": 780, 1106 | "wires": [ 1107 | [ 1108 | "338788c6045cabdb" 1109 | ] 1110 | ] 1111 | }, 1112 | { 1113 | "id": "5865e34fcf8bcd67", 1114 | "type": "ui_gauge", 1115 | "z": "a16955f928abc4fe", 1116 | "name": "", 1117 | "group": "b8ef5709b7b160fb", 1118 | "order": 4, 1119 | "width": "3", 1120 | "height": "3", 1121 | "gtype": "gage", 1122 | "title": "return", 1123 | "label": "fps", 1124 | "format": "{{value}}", 1125 | "min": 0, 1126 | "max": "15", 1127 | "colors": [ 1128 | "#ca3838", 1129 | "#e6e600", 1130 | "#00b500" 1131 | ], 1132 | "seg1": "4", 1133 | "seg2": "6", 1134 | "diff": false, 1135 | "className": "", 1136 | "x": 1680, 1137 | "y": 900, 1138 | "wires": [] 1139 | }, 1140 | { 1141 | "id": "b36cfb3e868804a9", 1142 | "type": "ui_gauge", 1143 | "z": "a16955f928abc4fe", 1144 | "name": "", 1145 | "group": "b8ef5709b7b160fb", 1146 | "order": 4, 1147 | "width": "3", 1148 | "height": "3", 1149 | "gtype": "gage", 1150 | "title": "analyze", 1151 | "label": "fps", 1152 | "format": "{{value}}", 1153 | "min": 0, 1154 | "max": "15", 1155 | "colors": [ 1156 | "#ca3838", 1157 | "#e6e600", 1158 | "#00b500" 1159 | ], 1160 | "seg1": "4", 1161 | "seg2": "6", 1162 | "diff": false, 1163 | "className": "", 1164 | "x": 1820, 1165 | "y": 840, 1166 | "wires": [] 1167 | }, 1168 | { 1169 | "id": "e425bcd90ea2576f", 1170 | "type": "ui_slider", 1171 | "z": "a16955f928abc4fe", 1172 | "name": "", 1173 | "label": "pos1", 1174 | "tooltip": "", 1175 | "group": "9530089c40af5601", 1176 | "order": 10, 1177 | "width": 0, 1178 | "height": 0, 1179 | "passthru": true, 1180 | "outs": "all", 1181 | "topic": "pos1", 1182 | "topicType": "str", 1183 | "min": 0, 1184 | "max": "100", 1185 | "step": 1, 1186 | "className": "", 1187 | "x": 770, 1188 | "y": 1080, 1189 | "wires": [ 1190 | [ 1191 | "df529567626e9faf" 1192 | ] 1193 | ] 1194 | }, 1195 | { 1196 | "id": "7931a4ae8d966d0b", 1197 | "type": "ui_slider", 1198 | "z": "a16955f928abc4fe", 1199 | "name": "", 1200 | "label": "pos2", 1201 | "tooltip": "", 1202 | "group": "9530089c40af5601", 1203 | "order": 11, 1204 | "width": 0, 1205 | "height": 0, 1206 | "passthru": true, 1207 | "outs": "all", 1208 | "topic": "pos2", 1209 | "topicType": "str", 1210 | "min": 0, 1211 | "max": "100", 1212 | "step": 1, 1213 | "className": "", 1214 | "x": 770, 1215 | "y": 1140, 1216 | "wires": [ 1217 | [ 1218 | "df529567626e9faf" 1219 | ] 1220 | ] 1221 | }, 1222 | { 1223 | "id": "abe2463022dd8455", 1224 | "type": "ui_switch", 1225 | "z": "a16955f928abc4fe", 1226 | "name": "vertical", 1227 | "label": "vertical", 1228 | "tooltip": "", 1229 | "group": "9530089c40af5601", 1230 | "order": 12, 1231 | "width": 0, 1232 | "height": 0, 1233 | "passthru": true, 1234 | "decouple": "false", 1235 | "topic": "vertical", 1236 | "topicType": "str", 1237 | "style": "", 1238 | "onvalue": "true", 1239 | "onvalueType": "bool", 1240 | "onicon": "", 1241 | "oncolor": "", 1242 | "offvalue": "false", 1243 | "offvalueType": "bool", 1244 | "officon": "", 1245 | "offcolor": "", 1246 | "animate": false, 1247 | "className": "", 1248 | "x": 740, 1249 | "y": 1200, 1250 | "wires": [ 1251 | [ 1252 | "df529567626e9faf" 1253 | ] 1254 | ] 1255 | }, 1256 | { 1257 | "id": "df529567626e9faf", 1258 | "type": "function", 1259 | "z": "a16955f928abc4fe", 1260 | "name": "setting", 1261 | "func": "\nswitch(msg.topic)\n{\n case \"pos1\":\n flow.set(\"pos1\", Math.round(msg.payload * 240 / 100));\n break;\n case \"pos2\":\n flow.set(\"pos2\", Math.round(msg.payload * 240 / 100));\n break;\n case \"vertical\":\n flow.set(\"vertical\", msg.payload);\n break;\n default:\n return msg;\n}\n\nreturn msg;", 1262 | "outputs": 1, 1263 | "timeout": 0, 1264 | "noerr": 0, 1265 | "initialize": "", 1266 | "finalize": "", 1267 | "libs": [], 1268 | "x": 960, 1269 | "y": 1140, 1270 | "wires": [ 1271 | [] 1272 | ] 1273 | }, 1274 | { 1275 | "id": "335acff635759144", 1276 | "type": "function", 1277 | "z": "a16955f928abc4fe", 1278 | "name": "draw", 1279 | "func": "\nif(flow.get(\"vertical\"))\n{\n msg.payload.regions_config.Region_A.polygon[1] = [240, 0];\n msg.payload.regions_config.Region_A.polygon[2] = [240, flow.get(\"pos2\")];\n msg.payload.regions_config.Region_A.polygon[3] = [0, flow.get(\"pos1\")];\n\n msg.payload.regions_config.Region_B.polygon[0] = [0, flow.get(\"pos1\")];\n msg.payload.regions_config.Region_B.polygon[1] = [240, flow.get(\"pos2\")];\n msg.payload.regions_config.Region_B.polygon[3] = [0, 240];\n}\nelse\n{\n msg.payload.regions_config.Region_A.polygon[1] = [flow.get(\"pos1\"), 0];\n msg.payload.regions_config.Region_A.polygon[2] = [flow.get(\"pos2\"), 240];\n msg.payload.regions_config.Region_A.polygon[3] = [0, 240];\n\n msg.payload.regions_config.Region_B.polygon[0] = [flow.get(\"pos1\"), 0];\n msg.payload.regions_config.Region_B.polygon[1] = [240, 0];\n msg.payload.regions_config.Region_B.polygon[3] = [flow.get(\"pos2\"), 240];\n}\n\nreturn msg;", 1280 | "outputs": 1, 1281 | "timeout": 0, 1282 | "noerr": 0, 1283 | "initialize": "", 1284 | "finalize": "", 1285 | "libs": [], 1286 | "x": 1010, 1287 | "y": 1000, 1288 | "wires": [ 1289 | [ 1290 | "e7dad17999087f9c", 1291 | "0ca99a1e20c907f5" 1292 | ] 1293 | ] 1294 | }, 1295 | { 1296 | "id": "2005160d7ce952b6", 1297 | "type": "msg-speed", 1298 | "z": "a16955f928abc4fe", 1299 | "name": "overlay", 1300 | "frequency": "sec", 1301 | "interval": 1, 1302 | "estimation": false, 1303 | "ignore": false, 1304 | "pauseAtStartup": false, 1305 | "topicDependent": false, 1306 | "x": 1800, 1307 | "y": 460, 1308 | "wires": [ 1309 | [ 1310 | "982f024e9730de25" 1311 | ], 1312 | [] 1313 | ] 1314 | }, 1315 | { 1316 | "id": "72d9c00611084f47", 1317 | "type": "msg-speed", 1318 | "z": "a16955f928abc4fe", 1319 | "name": "raw", 1320 | "frequency": "sec", 1321 | "interval": 1, 1322 | "estimation": false, 1323 | "ignore": false, 1324 | "pauseAtStartup": false, 1325 | "topicDependent": false, 1326 | "x": 1570, 1327 | "y": 380, 1328 | "wires": [ 1329 | [ 1330 | "38db7efa2f522462" 1331 | ], 1332 | [] 1333 | ] 1334 | }, 1335 | { 1336 | "id": "2c4b34705396492f", 1337 | "type": "msg-speed", 1338 | "z": "a16955f928abc4fe", 1339 | "name": "return", 1340 | "frequency": "sec", 1341 | "interval": 1, 1342 | "estimation": false, 1343 | "ignore": false, 1344 | "pauseAtStartup": false, 1345 | "topicDependent": false, 1346 | "x": 1460, 1347 | "y": 880, 1348 | "wires": [ 1349 | [ 1350 | "5865e34fcf8bcd67" 1351 | ], 1352 | [] 1353 | ] 1354 | }, 1355 | { 1356 | "id": "a0863aa0e9fa6a3a", 1357 | "type": "msg-speed", 1358 | "z": "a16955f928abc4fe", 1359 | "name": "analyze", 1360 | "frequency": "sec", 1361 | "interval": 1, 1362 | "estimation": false, 1363 | "ignore": false, 1364 | "pauseAtStartup": false, 1365 | "topicDependent": false, 1366 | "x": 1600, 1367 | "y": 800, 1368 | "wires": [ 1369 | [ 1370 | "b36cfb3e868804a9" 1371 | ], 1372 | [] 1373 | ] 1374 | }, 1375 | { 1376 | "id": "eeda5c5531438603", 1377 | "type": "ui_spacer", 1378 | "z": "a16955f928abc4fe", 1379 | "name": "spacer", 1380 | "group": "587f3511e9470eab", 1381 | "order": 1, 1382 | "width": 20, 1383 | "height": 1 1384 | }, 1385 | { 1386 | "id": "b66151316a38ce5f", 1387 | "type": "ui_spacer", 1388 | "z": "a16955f928abc4fe", 1389 | "name": "spacer", 1390 | "group": "587f3511e9470eab", 1391 | "order": 3, 1392 | "width": 2, 1393 | "height": 1 1394 | }, 1395 | { 1396 | "id": "9fdd365e7ec7d73c", 1397 | "type": "ui_spacer", 1398 | "z": "a16955f928abc4fe", 1399 | "name": "spacer", 1400 | "group": "587f3511e9470eab", 1401 | "order": 6, 1402 | "width": 2, 1403 | "height": 1 1404 | }, 1405 | { 1406 | "id": "966608a4ab923841", 1407 | "type": "ui_spacer", 1408 | "z": "a16955f928abc4fe", 1409 | "name": "spacer", 1410 | "group": "587f3511e9470eab", 1411 | "order": 9, 1412 | "width": 1, 1413 | "height": 1 1414 | }, 1415 | { 1416 | "id": "75f0918b5079a919", 1417 | "type": "ui_spacer", 1418 | "z": "a16955f928abc4fe", 1419 | "name": "spacer", 1420 | "group": "587f3511e9470eab", 1421 | "order": 12, 1422 | "width": 8, 1423 | "height": 1 1424 | }, 1425 | { 1426 | "id": "7cfeca3c2f53dd96", 1427 | "type": "ui_spacer", 1428 | "z": "a16955f928abc4fe", 1429 | "name": "spacer", 1430 | "group": "587f3511e9470eab", 1431 | "order": 16, 1432 | "width": 8, 1433 | "height": 1 1434 | }, 1435 | { 1436 | "id": "9d142a5b4d345fda", 1437 | "type": "ui_spacer", 1438 | "z": "a16955f928abc4fe", 1439 | "name": "spacer", 1440 | "group": "587f3511e9470eab", 1441 | "order": 18, 1442 | "width": 8, 1443 | "height": 1 1444 | }, 1445 | { 1446 | "id": "83590a7576ca88e6", 1447 | "type": "ui_spacer", 1448 | "z": "a16955f928abc4fe", 1449 | "name": "spacer", 1450 | "group": "587f3511e9470eab", 1451 | "order": 19, 1452 | "width": 8, 1453 | "height": 1 1454 | }, 1455 | { 1456 | "id": "d0f09a0b38d078c0", 1457 | "type": "ui_spacer", 1458 | "z": "a16955f928abc4fe", 1459 | "name": "spacer", 1460 | "group": "587f3511e9470eab", 1461 | "order": 21, 1462 | "width": 8, 1463 | "height": 1 1464 | }, 1465 | { 1466 | "id": "2f8769b930ec4031", 1467 | "type": "ui_spacer", 1468 | "z": "a16955f928abc4fe", 1469 | "name": "spacer", 1470 | "group": "587f3511e9470eab", 1471 | "order": 23, 1472 | "width": 1, 1473 | "height": 1 1474 | }, 1475 | { 1476 | "id": "0a4d91f7945889a8", 1477 | "type": "ui_spacer", 1478 | "z": "a16955f928abc4fe", 1479 | "name": "spacer", 1480 | "group": "587f3511e9470eab", 1481 | "order": 26, 1482 | "width": 1, 1483 | "height": 1 1484 | }, 1485 | { 1486 | "id": "ce0c372161caaec2", 1487 | "type": "ui_spacer", 1488 | "z": "a16955f928abc4fe", 1489 | "name": "spacer", 1490 | "group": "587f3511e9470eab", 1491 | "order": 28, 1492 | "width": 1, 1493 | "height": 1 1494 | }, 1495 | { 1496 | "id": "acf1dc743e83e04a", 1497 | "type": "ui_spacer", 1498 | "z": "a16955f928abc4fe", 1499 | "name": "spacer", 1500 | "group": "587f3511e9470eab", 1501 | "order": 30, 1502 | "width": 1, 1503 | "height": 1 1504 | }, 1505 | { 1506 | "id": "b1a14c6a351214a8", 1507 | "type": "ui_spacer", 1508 | "z": "a16955f928abc4fe", 1509 | "name": "spacer", 1510 | "group": "587f3511e9470eab", 1511 | "order": 31, 1512 | "width": 1, 1513 | "height": 1 1514 | }, 1515 | { 1516 | "id": "eefe917d27819ca4", 1517 | "type": "ui_spacer", 1518 | "z": "a16955f928abc4fe", 1519 | "name": "spacer", 1520 | "group": "587f3511e9470eab", 1521 | "order": 33, 1522 | "width": 5, 1523 | "height": 1 1524 | }, 1525 | { 1526 | "id": "81e7da1fae51617b", 1527 | "type": "ui_spacer", 1528 | "z": "a16955f928abc4fe", 1529 | "name": "spacer", 1530 | "group": "587f3511e9470eab", 1531 | "order": 34, 1532 | "width": 1, 1533 | "height": 1 1534 | }, 1535 | { 1536 | "id": "d8ee492a08f258e1", 1537 | "type": "ui_spacer", 1538 | "z": "a16955f928abc4fe", 1539 | "name": "spacer", 1540 | "group": "587f3511e9470eab", 1541 | "order": 36, 1542 | "width": 5, 1543 | "height": 1 1544 | }, 1545 | { 1546 | "id": "6d7b0c2f61470ddc", 1547 | "type": "ui_spacer", 1548 | "z": "a16955f928abc4fe", 1549 | "name": "spacer", 1550 | "group": "587f3511e9470eab", 1551 | "order": 37, 1552 | "width": 5, 1553 | "height": 1 1554 | }, 1555 | { 1556 | "id": "85da46f0992312a4", 1557 | "type": "ui_spacer", 1558 | "z": "a16955f928abc4fe", 1559 | "name": "spacer", 1560 | "group": "587f3511e9470eab", 1561 | "order": 38, 1562 | "width": 5, 1563 | "height": 1 1564 | }, 1565 | { 1566 | "id": "7b7b5e06d45b3b8d", 1567 | "type": "ui_spacer", 1568 | "z": "a16955f928abc4fe", 1569 | "name": "spacer", 1570 | "group": "587f3511e9470eab", 1571 | "order": 39, 1572 | "width": 4, 1573 | "height": 1 1574 | }, 1575 | { 1576 | "id": "65ca85fc4c5a4841", 1577 | "type": "ui_spacer", 1578 | "z": "a16955f928abc4fe", 1579 | "name": "spacer", 1580 | "group": "587f3511e9470eab", 1581 | "order": 41, 1582 | "width": 1, 1583 | "height": 1 1584 | }, 1585 | { 1586 | "id": "ecedc1fa674dcc73", 1587 | "type": "ui_spacer", 1588 | "z": "a16955f928abc4fe", 1589 | "name": "spacer", 1590 | "group": "587f3511e9470eab", 1591 | "order": 43, 1592 | "width": 2, 1593 | "height": 1 1594 | }, 1595 | { 1596 | "id": "21d57f18f896f1e1", 1597 | "type": "ui_spacer", 1598 | "z": "a16955f928abc4fe", 1599 | "name": "spacer", 1600 | "group": "587f3511e9470eab", 1601 | "order": 44, 1602 | "width": 5, 1603 | "height": 1 1604 | }, 1605 | { 1606 | "id": "d6b490e6f8f9b367", 1607 | "type": "ui_spacer", 1608 | "z": "a16955f928abc4fe", 1609 | "name": "spacer", 1610 | "group": "587f3511e9470eab", 1611 | "order": 45, 1612 | "width": 1, 1613 | "height": 1 1614 | }, 1615 | { 1616 | "id": "618147f3d54a3b6f", 1617 | "type": "ui_spacer", 1618 | "z": "a16955f928abc4fe", 1619 | "name": "spacer", 1620 | "group": "587f3511e9470eab", 1621 | "order": 46, 1622 | "width": 2, 1623 | "height": 1 1624 | }, 1625 | { 1626 | "id": "5057962592c1ab13", 1627 | "type": "ui_spacer", 1628 | "z": "a16955f928abc4fe", 1629 | "name": "spacer", 1630 | "group": "587f3511e9470eab", 1631 | "order": 47, 1632 | "width": 5, 1633 | "height": 1 1634 | }, 1635 | { 1636 | "id": "6da4ca1142f2dfc4", 1637 | "type": "ui_spacer", 1638 | "z": "a16955f928abc4fe", 1639 | "name": "spacer", 1640 | "group": "587f3511e9470eab", 1641 | "order": 48, 1642 | "width": 5, 1643 | "height": 1 1644 | }, 1645 | { 1646 | "id": "ae381be183704233", 1647 | "type": "ui_spacer", 1648 | "z": "a16955f928abc4fe", 1649 | "name": "spacer", 1650 | "group": "587f3511e9470eab", 1651 | "order": 49, 1652 | "width": 5, 1653 | "height": 1 1654 | }, 1655 | { 1656 | "id": "ee569638c468cdde", 1657 | "type": "ui_spacer", 1658 | "z": "a16955f928abc4fe", 1659 | "name": "spacer", 1660 | "group": "587f3511e9470eab", 1661 | "order": 50, 1662 | "width": 5, 1663 | "height": 1 1664 | }, 1665 | { 1666 | "id": "dac9aa726ffa8bb1", 1667 | "type": "ui_spacer", 1668 | "z": "a16955f928abc4fe", 1669 | "name": "spacer", 1670 | "group": "587f3511e9470eab", 1671 | "order": 51, 1672 | "width": 5, 1673 | "height": 1 1674 | }, 1675 | { 1676 | "id": "54c40a45809b3ad5", 1677 | "type": "ui_spacer", 1678 | "z": "a16955f928abc4fe", 1679 | "name": "spacer", 1680 | "group": "587f3511e9470eab", 1681 | "order": 52, 1682 | "width": 5, 1683 | "height": 1 1684 | }, 1685 | { 1686 | "id": "26032b246e1c6198", 1687 | "type": "ui_spacer", 1688 | "z": "a16955f928abc4fe", 1689 | "name": "spacer", 1690 | "group": "587f3511e9470eab", 1691 | "order": 53, 1692 | "width": 5, 1693 | "height": 1 1694 | }, 1695 | { 1696 | "id": "d2be343521a8dca4", 1697 | "type": "ui_spacer", 1698 | "z": "a16955f928abc4fe", 1699 | "name": "spacer", 1700 | "group": "587f3511e9470eab", 1701 | "order": 54, 1702 | "width": 5, 1703 | "height": 1 1704 | }, 1705 | { 1706 | "id": "56dfcc9f930f8566", 1707 | "type": "ui_spacer", 1708 | "z": "a16955f928abc4fe", 1709 | "name": "spacer", 1710 | "group": "587f3511e9470eab", 1711 | "order": 55, 1712 | "width": 5, 1713 | "height": 1 1714 | }, 1715 | { 1716 | "id": "343dd6ef761ece95", 1717 | "type": "ui_spacer", 1718 | "z": "a16955f928abc4fe", 1719 | "name": "spacer", 1720 | "group": "587f3511e9470eab", 1721 | "order": 56, 1722 | "width": 5, 1723 | "height": 1 1724 | }, 1725 | { 1726 | "id": "d91f45f10a66c170", 1727 | "type": "ui_spacer", 1728 | "z": "a16955f928abc4fe", 1729 | "name": "spacer", 1730 | "group": "587f3511e9470eab", 1731 | "order": 57, 1732 | "width": 5, 1733 | "height": 1 1734 | }, 1735 | { 1736 | "id": "c772aa5f0613d6a7", 1737 | "type": "ui_spacer", 1738 | "z": "a16955f928abc4fe", 1739 | "name": "spacer", 1740 | "group": "587f3511e9470eab", 1741 | "order": 58, 1742 | "width": 2, 1743 | "height": 1 1744 | }, 1745 | { 1746 | "id": "a5720a2961bfbd50", 1747 | "type": "ui_spacer", 1748 | "z": "a16955f928abc4fe", 1749 | "name": "spacer", 1750 | "group": "587f3511e9470eab", 1751 | "order": 60, 1752 | "width": 2, 1753 | "height": 1 1754 | }, 1755 | { 1756 | "id": "2f5df6375b12cd37", 1757 | "type": "ui_spacer", 1758 | "z": "a16955f928abc4fe", 1759 | "name": "spacer", 1760 | "group": "587f3511e9470eab", 1761 | "order": 61, 1762 | "width": 2, 1763 | "height": 1 1764 | }, 1765 | { 1766 | "id": "d29c4e786a8abeb7", 1767 | "type": "ui_spacer", 1768 | "z": "a16955f928abc4fe", 1769 | "name": "spacer", 1770 | "group": "587f3511e9470eab", 1771 | "order": 62, 1772 | "width": 2, 1773 | "height": 1 1774 | }, 1775 | { 1776 | "id": "39ab262634b243a3", 1777 | "type": "ui_spacer", 1778 | "z": "a16955f928abc4fe", 1779 | "name": "spacer", 1780 | "group": "587f3511e9470eab", 1781 | "order": 63, 1782 | "width": 2, 1783 | "height": 1 1784 | }, 1785 | { 1786 | "id": "0686dc96b9adf5aa", 1787 | "type": "ui_spacer", 1788 | "z": "a16955f928abc4fe", 1789 | "name": "spacer", 1790 | "group": "587f3511e9470eab", 1791 | "order": 64, 1792 | "width": 2, 1793 | "height": 1 1794 | }, 1795 | { 1796 | "id": "4e4e285bee946301", 1797 | "type": "ui_spacer", 1798 | "z": "a16955f928abc4fe", 1799 | "name": "spacer", 1800 | "group": "587f3511e9470eab", 1801 | "order": 65, 1802 | "width": 2, 1803 | "height": 1 1804 | }, 1805 | { 1806 | "id": "c3246633f102ba29", 1807 | "type": "ui_spacer", 1808 | "z": "a16955f928abc4fe", 1809 | "name": "spacer", 1810 | "group": "587f3511e9470eab", 1811 | "order": 66, 1812 | "width": 2, 1813 | "height": 1 1814 | }, 1815 | { 1816 | "id": "bf0db97571c21d63", 1817 | "type": "ui_spacer", 1818 | "z": "a16955f928abc4fe", 1819 | "name": "spacer", 1820 | "group": "587f3511e9470eab", 1821 | "order": 67, 1822 | "width": 2, 1823 | "height": 1 1824 | }, 1825 | { 1826 | "id": "db7047aa10757248", 1827 | "type": "ui_spacer", 1828 | "z": "a16955f928abc4fe", 1829 | "name": "spacer", 1830 | "group": "587f3511e9470eab", 1831 | "order": 68, 1832 | "width": 2, 1833 | "height": 1 1834 | }, 1835 | { 1836 | "id": "8b6ceb218ebf0daa", 1837 | "type": "ui_spacer", 1838 | "z": "a16955f928abc4fe", 1839 | "name": "spacer", 1840 | "group": "587f3511e9470eab", 1841 | "order": 69, 1842 | "width": 2, 1843 | "height": 1 1844 | }, 1845 | { 1846 | "id": "fee194bdde04209c", 1847 | "type": "ui_spacer", 1848 | "z": "a16955f928abc4fe", 1849 | "name": "spacer", 1850 | "group": "587f3511e9470eab", 1851 | "order": 70, 1852 | "width": 2, 1853 | "height": 1 1854 | }, 1855 | { 1856 | "id": "b17ddb4c23da0d44", 1857 | "type": "ui_spacer", 1858 | "z": "a16955f928abc4fe", 1859 | "name": "spacer", 1860 | "group": "587f3511e9470eab", 1861 | "order": 71, 1862 | "width": 2, 1863 | "height": 1 1864 | }, 1865 | { 1866 | "id": "6b39e65201cb0313", 1867 | "type": "ui_spacer", 1868 | "z": "a16955f928abc4fe", 1869 | "name": "spacer", 1870 | "group": "587f3511e9470eab", 1871 | "order": 73, 1872 | "width": 2, 1873 | "height": 1 1874 | }, 1875 | { 1876 | "id": "601cf57d0d2915d5", 1877 | "type": "ui_spacer", 1878 | "z": "a16955f928abc4fe", 1879 | "name": "spacer", 1880 | "group": "587f3511e9470eab", 1881 | "order": 74, 1882 | "width": 2, 1883 | "height": 1 1884 | }, 1885 | { 1886 | "id": "d463a3faaa9bbeb3", 1887 | "type": "ui_spacer", 1888 | "z": "a16955f928abc4fe", 1889 | "name": "spacer", 1890 | "group": "587f3511e9470eab", 1891 | "order": 75, 1892 | "width": 2, 1893 | "height": 1 1894 | }, 1895 | { 1896 | "id": "837189ca317d5174", 1897 | "type": "ui_spacer", 1898 | "z": "a16955f928abc4fe", 1899 | "name": "spacer", 1900 | "group": "587f3511e9470eab", 1901 | "order": 76, 1902 | "width": 2, 1903 | "height": 1 1904 | }, 1905 | { 1906 | "id": "47e74cf70f901480", 1907 | "type": "ui_spacer", 1908 | "z": "a16955f928abc4fe", 1909 | "name": "spacer", 1910 | "group": "587f3511e9470eab", 1911 | "order": 77, 1912 | "width": 2, 1913 | "height": 1 1914 | }, 1915 | { 1916 | "id": "c7ee8678e7545e5e", 1917 | "type": "ui_spacer", 1918 | "z": "a16955f928abc4fe", 1919 | "name": "spacer", 1920 | "group": "587f3511e9470eab", 1921 | "order": 78, 1922 | "width": 2, 1923 | "height": 1 1924 | }, 1925 | { 1926 | "id": "ee8873fcdb826d9e", 1927 | "type": "ui_spacer", 1928 | "z": "a16955f928abc4fe", 1929 | "name": "spacer", 1930 | "group": "587f3511e9470eab", 1931 | "order": 79, 1932 | "width": 2, 1933 | "height": 1 1934 | }, 1935 | { 1936 | "id": "b1f73b7abe1c2db7", 1937 | "type": "ui_spacer", 1938 | "z": "a16955f928abc4fe", 1939 | "name": "spacer", 1940 | "group": "587f3511e9470eab", 1941 | "order": 80, 1942 | "width": 2, 1943 | "height": 1 1944 | }, 1945 | { 1946 | "id": "cd2df1606541e32d", 1947 | "type": "ui_spacer", 1948 | "z": "a16955f928abc4fe", 1949 | "name": "spacer", 1950 | "group": "587f3511e9470eab", 1951 | "order": 81, 1952 | "width": 2, 1953 | "height": 1 1954 | }, 1955 | { 1956 | "id": "8441d4244b560164", 1957 | "type": "mqtt-broker", 1958 | "name": "", 1959 | "broker": "192.168.49.104", 1960 | "port": "1883", 1961 | "clientid": "", 1962 | "autoConnect": true, 1963 | "usetls": false, 1964 | "protocolVersion": "4", 1965 | "keepalive": "60", 1966 | "cleansession": true, 1967 | "autoUnsubscribe": true, 1968 | "birthTopic": "", 1969 | "birthQos": "0", 1970 | "birthRetain": "false", 1971 | "birthPayload": "", 1972 | "birthMsg": {}, 1973 | "closeTopic": "", 1974 | "closeQos": "0", 1975 | "closeRetain": "false", 1976 | "closePayload": "", 1977 | "closeMsg": {}, 1978 | "willTopic": "", 1979 | "willQos": "0", 1980 | "willRetain": "false", 1981 | "willPayload": "", 1982 | "willMsg": {}, 1983 | "userProps": "", 1984 | "sessionExpiry": "" 1985 | }, 1986 | { 1987 | "id": "9530089c40af5601", 1988 | "type": "ui_group", 1989 | "name": "SSCMA", 1990 | "tab": "9ffee9ecdba42e5a", 1991 | "order": 1, 1992 | "disp": true, 1993 | "width": "6", 1994 | "collapse": false, 1995 | "className": "" 1996 | }, 1997 | { 1998 | "id": "b8ef5709b7b160fb", 1999 | "type": "ui_group", 2000 | "name": "Performance", 2001 | "tab": "9ffee9ecdba42e5a", 2002 | "order": 3, 2003 | "disp": true, 2004 | "width": "6", 2005 | "collapse": false, 2006 | "className": "" 2007 | }, 2008 | { 2009 | "id": "587f3511e9470eab", 2010 | "type": "ui_group", 2011 | "name": "Default", 2012 | "tab": "278e2f0e45278210", 2013 | "order": 1, 2014 | "disp": false, 2015 | "width": 20, 2016 | "collapse": false, 2017 | "className": "" 2018 | }, 2019 | { 2020 | "id": "d783cb980c3979fc", 2021 | "type": "ui_group", 2022 | "name": "Demo", 2023 | "tab": "0449b3b1fea99d52", 2024 | "order": 1, 2025 | "disp": true, 2026 | "width": "6", 2027 | "collapse": false 2028 | }, 2029 | { 2030 | "id": "9ffee9ecdba42e5a", 2031 | "type": "ui_tab", 2032 | "name": "Home", 2033 | "icon": "dashboard", 2034 | "disabled": false, 2035 | "hidden": false 2036 | }, 2037 | { 2038 | "id": "278e2f0e45278210", 2039 | "type": "ui_tab", 2040 | "name": "Mirror", 2041 | "icon": "dashboard", 2042 | "order": 2, 2043 | "disabled": false, 2044 | "hidden": false 2045 | }, 2046 | { 2047 | "id": "0449b3b1fea99d52", 2048 | "type": "ui_tab", 2049 | "name": "Demo", 2050 | "icon": "dashboard", 2051 | "disabled": false, 2052 | "hidden": false 2053 | } 2054 | ] -------------------------------------------------------------------------------- /Software/node-red/main_flow.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "14ce0cc2506ae0e4", 4 | "type": "tab", 5 | "label": "Flow 1", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "4d53565a2e31d732", 11 | "type": "inject", 12 | "z": "14ce0cc2506ae0e4", 13 | "name": "", 14 | "props": [ 15 | { 16 | "p": "payload" 17 | } 18 | ], 19 | "repeat": "60", 20 | "crontab": "", 21 | "once": true, 22 | "onceDelay": "1", 23 | "topic": "", 24 | "payload": "true", 25 | "payloadType": "bool", 26 | "x": 170, 27 | "y": 280, 28 | "wires": [ 29 | [ 30 | "3780445e34a7c5d1", 31 | "f93a18da2118731a", 32 | "1d7ce67c0a72f6c8", 33 | "3d0162eabcdc321d", 34 | "db848066923e7569", 35 | "d1c0578545c348fb" 36 | ] 37 | ] 38 | }, 39 | { 40 | "id": "f5700c5d151aa067", 41 | "type": "comment", 42 | "z": "14ce0cc2506ae0e4", 43 | "name": "OpenWeatherMap One Call API for Weather and Forecast", 44 | "info": "", 45 | "x": 310, 46 | "y": 540, 47 | "wires": [] 48 | }, 49 | { 50 | "id": "7d483dcb84df19b9", 51 | "type": "function", 52 | "z": "14ce0cc2506ae0e4", 53 | "name": "Format forecast data", 54 | "func": "var fcdata = {};\nvar units=flow.get('units');\nif (units===undefined)\n{\n units=\"imperial\";\n}\n\nfunction formatTemp(high, low){\n if (units == \"imperial\") {\n if (low){\n temp = parseFloat(high).toFixed() + '/' + parseFloat(low).toFixed()\n }\n else {\n temp = parseFloat(high).toFixed() + '°F'\n }\n }\n else { // metric\n if (low){\n temp = parseFloat(high).toFixed() + '/' + parseFloat(low).toFixed()\n }\n else {\n temp = parseFloat(high).toFixed() + '°C'\n }\n }\n return temp;\n}\n\nfunction dayName(unixTime){\n var dateObject = new Date(unixTime * 1000);\n return dateObject.toLocaleString(flow.get('lang'), { timezone: msg.payload.timezone, weekday: 'short'});\n // If the line above is not producing the correct short weekday names for the language set by 'lang',\n // you can try upgrading to NodeJS version 13 or higher (when full international support was added for the toLocaleString function),\n // or you can use the code below which is an example for French short weekday names.\n/* \n switch (dateObject.toLocaleString('en', { timezone: msg.payload.timezone, weekday: 'short'})) {\n case 'Mon':\n return 'Lun';\n case 'Tue':\n return 'Mar';\n case 'Wed':\n return 'Mer';\n case 'Thu':\n return 'Jeu';\n case 'Fri':\n return 'Ven';\n case 'Sat':\n return 'Sam';\n case 'Sun':\n return 'Dim';\n }\n*/\n}\n\nfunction timeConvert(UNIX_timestamp){\n// Convert the Unix timestamp to a JavaScript Date object\n var dateObject = new Date(UNIX_timestamp * 1000); // Multiply by 1000 if the timestamp is in seconds\n\n// Use Intl.DateTimeFormat to format the date\nconst options = {\n hour: 'numeric',\n hour12: true, timeZone: msg.payload.timezone\n};\n\n return Intl.DateTimeFormat('en-US', options).format(dateObject);\n}\n\n// prepare forecast data for CSS based ui widget\nfcdata.payload = {\n rowtext: {\n \tdata01: {\n cell01: timeConvert(msg.payload.hourly[1].dt),\n cell02: timeConvert(msg.payload.hourly[2].dt),\n cell03: timeConvert(msg.payload.hourly[3].dt),\n cell04: timeConvert(msg.payload.hourly[4].dt),\n // cell05: timeConvert(msg.payload.hourly[5].dt),\n // cell06: timeConvert(msg.payload.hourly[6].dt),\n cell07: dayName(msg.payload.daily[1].dt),\n cell08: dayName(msg.payload.daily[2].dt),\n cell09: dayName(msg.payload.daily[3].dt),\n cell10: dayName(msg.payload.daily[4].dt),\n \t},\n \tdata02: {\n cell01: formatTemp(msg.payload.hourly[1].temp),\n cell02: formatTemp(msg.payload.hourly[2].temp),\n cell03: formatTemp(msg.payload.hourly[3].temp),\n cell04: formatTemp(msg.payload.hourly[4].temp),\n // cell05: formatTemp(msg.payload.hourly[5].temp),\n // cell06: formatTemp(msg.payload.hourly[6].temp),\n cell07: formatTemp(msg.payload.daily[1].temp.max, msg.payload.daily[0].temp.min),\n cell08: formatTemp(msg.payload.daily[2].temp.max, msg.payload.daily[1].temp.min),\n cell09: formatTemp(msg.payload.daily[3].temp.max, msg.payload.daily[2].temp.min),\n cell10: formatTemp(msg.payload.daily[4].temp.max, msg.payload.daily[3].temp.min),\n \t}\n },\n rowicons: {\n \tdata01: {\n \t\tcell01: msg.payload.hourly[1].weather[0].icon,\n \t\tcell02: msg.payload.hourly[2].weather[0].icon,\n \t\tcell03: msg.payload.hourly[3].weather[0].icon,\n \t\tcell04: msg.payload.hourly[4].weather[0].icon,\n \t\t// cell05: msg.payload.hourly[5].weather[0].icon,\n \t\t// cell06: msg.payload.hourly[6].weather[0].icon,\n \t\tcell07: msg.payload.daily[1].weather[0].icon,\n \t\tcell08: msg.payload.daily[2].weather[0].icon,\n \t\tcell09: msg.payload.daily[3].weather[0].icon,\n \t\tcell10: msg.payload.daily[4].weather[0].icon,\n \t}\n }\n}\n\nreturn fcdata;", 55 | "outputs": 1, 56 | "timeout": "", 57 | "noerr": 10, 58 | "initialize": "", 59 | "finalize": "", 60 | "libs": [], 61 | "x": 760, 62 | "y": 760, 63 | "wires": [ 64 | [ 65 | "2eba8e74a0ded4f5", 66 | "e14a631c1ace4905" 67 | ] 68 | ] 69 | }, 70 | { 71 | "id": "bc62329b2f6d4125", 72 | "type": "http request", 73 | "z": "14ce0cc2506ae0e4", 74 | "name": "Get OWM data", 75 | "method": "GET", 76 | "ret": "obj", 77 | "paytoqs": "query", 78 | "url": "https://api.openweathermap.org/data/2.5/onecall", 79 | "tls": "", 80 | "persist": false, 81 | "proxy": "", 82 | "insecureHTTPParser": false, 83 | "authType": "", 84 | "senderr": false, 85 | "headers": [], 86 | "x": 580, 87 | "y": 640, 88 | "wires": [ 89 | [ 90 | "452b6b94b22c1e38", 91 | "7d483dcb84df19b9", 92 | "9429561f2822a9a1" 93 | ] 94 | ] 95 | }, 96 | { 97 | "id": "3b6f2af1b795676a", 98 | "type": "inject", 99 | "z": "14ce0cc2506ae0e4", 100 | "name": "Trigger", 101 | "props": [ 102 | { 103 | "p": "payload" 104 | }, 105 | { 106 | "p": "topic", 107 | "vt": "str" 108 | } 109 | ], 110 | "repeat": "3600", 111 | "crontab": "", 112 | "once": true, 113 | "onceDelay": "1", 114 | "topic": "", 115 | "payload": "true", 116 | "payloadType": "bool", 117 | "x": 180, 118 | "y": 640, 119 | "wires": [ 120 | [ 121 | "acc67efaaa625746" 122 | ] 123 | ] 124 | }, 125 | { 126 | "id": "452b6b94b22c1e38", 127 | "type": "function", 128 | "z": "14ce0cc2506ae0e4", 129 | "name": "Format current data", 130 | "func": "var icon = {};\n\nvar units = flow.get('units');\nif (units === undefined) {units = 'imperial';}\n\nfunction timeConvert(UNIX_timestamp){\n// Convert the Unix timestamp to a JavaScript Date object\n var dateObject = new Date(UNIX_timestamp * 1000); // Multiply by 1000 if the timestamp is in seconds\n\n// Use Intl.DateTimeFormat to format the date\nconst options = {\n hour: '2-digit', minute: '2-digit', second: '2-digit',\n hour12: true, timeZone: msg.payload.timezone\n};\n\n return Intl.DateTimeFormat('en-US', options).format(dateObject);\n}\n\nvar degreesToCardinal = function(deg){\n if (deg>11.25 && deg<=33.75){return \"NNE\";}\n else if (deg>33.75 && deg<56.25){return \"NE\";}\n else if (deg>56.25 && deg<78.75){return \"ENE\";}\n else if (deg>78.75 && deg<101.25){return \"E\";}\n else if (deg>101.25 && deg<123.75){return \"ESE\";}\n else if (deg>123.75 && deg<146.25){return \"SE\";}\n else if (deg>146.25 && deg<168.75){return \"SSE\";}\n else if (deg>168.75 && deg<191.25){return \"S\";}\n else if (deg>191.25 && deg<213.75){return \"SSW\";}\n else if (deg>213.75 && deg<236.25){return \"SW\";}\n else if (deg>236.25 && deg<258.75){return \"WSW\";}\n else if (deg>258.75 && deg<281.25){return \"W\";}\n else if (deg>281.25 && deg<303.75){return \"WNW\";}\n else if (deg>303.75 && deg<326.25){return \"NW\";}\n else if (deg>326.25 && deg<348.75){return \"NNW\";}\n else {return \"N\";}\n}\n\nif (units == \"imperial\")\n{\n msg.payload.current.temp = msg.payload.current.temp.toFixed() + '°';\n msg.payload.current.wind_speed = msg.payload.current.wind_speed.toFixed() + ' mph';\n}\nelse // metric units\n{\n msg.payload.current.temp = msg.payload.current.temp.toFixed(1) + '°';\n msg.payload.current.wind_speed = msg.payload.current.wind_speed.toFixed(1) + ' m/s';\n}\n\nmsg.payload.current.wind_cardinal = degreesToCardinal(msg.payload.current.wind_deg);\n\nmsg.payload.current.sunrise = timeConvert(msg.payload.current.sunrise);\nmsg.payload.current.sunset = timeConvert(msg.payload.current.sunset);\n\nvar iconString = 'wi wi-owm-' + msg.payload.current.weather[0].icon + ' wi-4x';\nicon = {\n ui_control: {\n icon: iconString\n }\n}; \nnode.warn(icon);\nreturn [msg, {ui_control: {icon: iconString}}];\n//return [msg, icon];\n\n", 131 | "outputs": 2, 132 | "timeout": "", 133 | "noerr": 0, 134 | "initialize": "", 135 | "finalize": "", 136 | "libs": [], 137 | "x": 810, 138 | "y": 640, 139 | "wires": [ 140 | [ 141 | "cf61925f71764024", 142 | "6aa891ced7bdbae9", 143 | "7d8167469cc6011f", 144 | "54f9d1e8673e7632", 145 | "406acc32b466c5ca", 146 | "9625e20537d793b1" 147 | ], 148 | [ 149 | "1cdedf4bf06e7f7f" 150 | ] 151 | ] 152 | }, 153 | { 154 | "id": "acc67efaaa625746", 155 | "type": "change", 156 | "z": "14ce0cc2506ae0e4", 157 | "name": "Settings", 158 | "rules": [ 159 | { 160 | "t": "delete", 161 | "p": "payload", 162 | "pt": "msg" 163 | }, 164 | { 165 | "t": "set", 166 | "p": "payload.lat", 167 | "pt": "msg", 168 | "to": "22.5455", 169 | "tot": "str" 170 | }, 171 | { 172 | "t": "set", 173 | "p": "payload.lon", 174 | "pt": "msg", 175 | "to": "114.0683", 176 | "tot": "str" 177 | }, 178 | { 179 | "t": "set", 180 | "p": "payload.appid", 181 | "pt": "msg", 182 | "to": "b201736f1f6f0caa388c80dfaa7ab0f9", 183 | "tot": "str" 184 | }, 185 | { 186 | "t": "set", 187 | "p": "payload.units", 188 | "pt": "msg", 189 | "to": "metric", 190 | "tot": "str" 191 | }, 192 | { 193 | "t": "set", 194 | "p": "payload.lang", 195 | "pt": "msg", 196 | "to": "en", 197 | "tot": "str" 198 | }, 199 | { 200 | "t": "set", 201 | "p": "hour12", 202 | "pt": "flow", 203 | "to": "false", 204 | "tot": "bool" 205 | }, 206 | { 207 | "t": "set", 208 | "p": "units", 209 | "pt": "flow", 210 | "to": "payload.units", 211 | "tot": "msg" 212 | }, 213 | { 214 | "t": "set", 215 | "p": "lang", 216 | "pt": "flow", 217 | "to": "payload.lang", 218 | "tot": "msg" 219 | }, 220 | { 221 | "t": "set", 222 | "p": "payload.exclude", 223 | "pt": "msg", 224 | "to": "minutely", 225 | "tot": "str" 226 | } 227 | ], 228 | "action": "", 229 | "property": "", 230 | "from": "", 231 | "to": "", 232 | "reg": false, 233 | "x": 380, 234 | "y": 640, 235 | "wires": [ 236 | [ 237 | "bc62329b2f6d4125" 238 | ] 239 | ] 240 | }, 241 | { 242 | "id": "2001a88d4e694515", 243 | "type": "link out", 244 | "z": "14ce0cc2506ae0e4", 245 | "name": "Refresh", 246 | "links": [ 247 | "be73cac8a75d117c", 248 | "32414a721cdb256f" 249 | ], 250 | "x": 1195, 251 | "y": 840, 252 | "wires": [] 253 | }, 254 | { 255 | "id": "be73cac8a75d117c", 256 | "type": "link in", 257 | "z": "14ce0cc2506ae0e4", 258 | "name": "", 259 | "links": [ 260 | "2001a88d4e694515", 261 | "b99550dfe4764782" 262 | ], 263 | "x": 215, 264 | "y": 700, 265 | "wires": [ 266 | [ 267 | "acc67efaaa625746" 268 | ] 269 | ] 270 | }, 271 | { 272 | "id": "6afcc93d37c8a315", 273 | "type": "switch", 274 | "z": "14ce0cc2506ae0e4", 275 | "name": "tab focus", 276 | "property": "tab", 277 | "propertyType": "msg", 278 | "rules": [ 279 | { 280 | "t": "eq", 281 | "v": "1", 282 | "vt": "num" 283 | } 284 | ], 285 | "checkall": "true", 286 | "repair": false, 287 | "outputs": 1, 288 | "x": 320, 289 | "y": 760, 290 | "wires": [ 291 | [ 292 | "acc67efaaa625746" 293 | ] 294 | ] 295 | }, 296 | { 297 | "id": "9625e20537d793b1", 298 | "type": "debug", 299 | "z": "14ce0cc2506ae0e4", 300 | "name": "debug 5", 301 | "active": false, 302 | "tosidebar": true, 303 | "console": false, 304 | "tostatus": false, 305 | "complete": "false", 306 | "statusVal": "", 307 | "statusType": "auto", 308 | "x": 1080, 309 | "y": 560, 310 | "wires": [] 311 | }, 312 | { 313 | "id": "9429561f2822a9a1", 314 | "type": "debug", 315 | "z": "14ce0cc2506ae0e4", 316 | "name": "debug 6", 317 | "active": false, 318 | "tosidebar": true, 319 | "console": false, 320 | "tostatus": false, 321 | "complete": "false", 322 | "statusVal": "", 323 | "statusType": "auto", 324 | "x": 940, 325 | "y": 520, 326 | "wires": [] 327 | }, 328 | { 329 | "id": "3d0162eabcdc321d", 330 | "type": "function", 331 | "z": "14ce0cc2506ae0e4", 332 | "name": "System Time", 333 | "func": "var now = new Date();\n\n // Use Intl.DateTimeFormat to format the date\n const options = {\n hour: '2-digit', minute: '2-digit',\n hour12: true, timeZone: \"Asia/Shanghai\"\n };\n\nmsg.payload =Intl.DateTimeFormat('en-US', options).format(now);\n\nreturn msg;\n", 334 | "outputs": 1, 335 | "timeout": 0, 336 | "noerr": 0, 337 | "initialize": "", 338 | "finalize": "", 339 | "libs": [], 340 | "x": 430, 341 | "y": 160, 342 | "wires": [ 343 | [ 344 | "73d31b2869c35344" 345 | ] 346 | ] 347 | }, 348 | { 349 | "id": "db848066923e7569", 350 | "type": "function", 351 | "z": "14ce0cc2506ae0e4", 352 | "name": "Local Date", 353 | "func": "var now = new Date();\nvar days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\nvar months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\n\nvar day = days[now.getDay()];\nvar month = months[now.getMonth()];\nvar date = now.getDate();\nvar year = now.getFullYear();\n\nmsg.payload = day + ', ' + month + ' ' + date + ', ' + year;\nreturn msg;", 354 | "outputs": 1, 355 | "timeout": 0, 356 | "noerr": 0, 357 | "initialize": "", 358 | "finalize": "", 359 | "libs": [], 360 | "x": 430, 361 | "y": 200, 362 | "wires": [ 363 | [ 364 | "37983d4eb44685bf" 365 | ] 366 | ] 367 | }, 368 | { 369 | "id": "6d7e9bec6b830ce2", 370 | "type": "comment", 371 | "z": "14ce0cc2506ae0e4", 372 | "name": "Getting sensor data from HA", 373 | "info": "", 374 | "x": 220, 375 | "y": 100, 376 | "wires": [] 377 | }, 378 | { 379 | "id": "3861c4969aa551ef", 380 | "type": "mqtt in", 381 | "z": "14ce0cc2506ae0e4", 382 | "name": "", 383 | "topic": "/human", 384 | "qos": "2", 385 | "datatype": "auto-detect", 386 | "broker": "592452ad925527a7", 387 | "nl": false, 388 | "rap": true, 389 | "rh": 0, 390 | "inputs": 0, 391 | "x": 150, 392 | "y": 1160, 393 | "wires": [ 394 | [ 395 | "ada044be7935c7b4" 396 | ] 397 | ] 398 | }, 399 | { 400 | "id": "ada044be7935c7b4", 401 | "type": "function", 402 | "z": "14ce0cc2506ae0e4", 403 | "name": "function 13", 404 | "func": "var currentTime = Intl.DateTimeFormat('en-US', { hour: '2-digit', minute: '2-digit', hour12: true, timeZone: \"Asia/Shanghai\" }).format(new Date());\n\nvar newMsg = {\n name: msg.topic === \"/human\" ? 'human' : 'Seeebot',\n text: msg.payload,\n side: msg.topic === \"/human\" ? 'right' : 'left', // 'right' for human, 'left' for seeebot\n timestamp: currentTime,\n uniqueKey: new Date().getTime().toString(),\n typingEffect: msg.payload.length > 1\n};\n\nnode.send({ payload: newMsg });\nreturn null;\n", 405 | "outputs": 1, 406 | "timeout": 0, 407 | "noerr": 0, 408 | "initialize": "", 409 | "finalize": "", 410 | "libs": [], 411 | "x": 390, 412 | "y": 1140, 413 | "wires": [ 414 | [ 415 | "ed1fc54b53cb9c4b" 416 | ] 417 | ] 418 | }, 419 | { 420 | "id": "ed1fc54b53cb9c4b", 421 | "type": "function", 422 | "z": "14ce0cc2506ae0e4", 423 | "name": "function 14", 424 | "func": "var allMessages = flow.get('allMessages') || [];\n\n// Check if the message has a reset command\nif (msg.reset === true) {\n // Clear the stored messages\n allMessages = [];\n} else {\n // Add the new message\n allMessages.push(msg.payload);\n\n // Sort messages by timestamp\n allMessages.sort((a, b) => a.timestamp.localeCompare(b.timestamp));\n\n // Keep only the last four messages\n if (allMessages.length > 4) {\n allMessages = allMessages.slice(-4);\n }\n}\n\n// Save the updated messages array\nflow.set('allMessages', allMessages);\n\n// Output the entire message array\nmsg.payload = allMessages;\nreturn msg;\n", 425 | "outputs": 1, 426 | "timeout": 0, 427 | "noerr": 0, 428 | "initialize": "", 429 | "finalize": "", 430 | "libs": [], 431 | "x": 610, 432 | "y": 1140, 433 | "wires": [ 434 | [ 435 | "c863b9b6e5153681", 436 | "475828b3030c9cdc" 437 | ] 438 | ] 439 | }, 440 | { 441 | "id": "475828b3030c9cdc", 442 | "type": "debug", 443 | "z": "14ce0cc2506ae0e4", 444 | "name": "debug 21", 445 | "active": false, 446 | "tosidebar": true, 447 | "console": false, 448 | "tostatus": false, 449 | "complete": "false", 450 | "statusVal": "", 451 | "statusType": "auto", 452 | "x": 820, 453 | "y": 1060, 454 | "wires": [] 455 | }, 456 | { 457 | "id": "8347b875552b29f2", 458 | "type": "debug", 459 | "z": "14ce0cc2506ae0e4", 460 | "name": "debug 22", 461 | "active": true, 462 | "tosidebar": true, 463 | "console": false, 464 | "tostatus": false, 465 | "complete": "false", 466 | "statusVal": "", 467 | "statusType": "auto", 468 | "x": 1120, 469 | "y": 1100, 470 | "wires": [] 471 | }, 472 | { 473 | "id": "e27faa7b507fbd01", 474 | "type": "mqtt in", 475 | "z": "14ce0cc2506ae0e4", 476 | "name": "", 477 | "topic": "/seeebot", 478 | "qos": "2", 479 | "datatype": "auto-detect", 480 | "broker": "592452ad925527a7", 481 | "nl": false, 482 | "rap": true, 483 | "rh": 0, 484 | "inputs": 0, 485 | "x": 160, 486 | "y": 1220, 487 | "wires": [ 488 | [ 489 | "ada044be7935c7b4", 490 | "4a22517b04b5caf6" 491 | ] 492 | ] 493 | }, 494 | { 495 | "id": "4a22517b04b5caf6", 496 | "type": "function", 497 | "z": "14ce0cc2506ae0e4", 498 | "name": "function 15", 499 | "func": "// Assuming the payload is a string\nvar message = msg.payload;\nvar displayTimePerCharacter = 45; // milliseconds per character\nvar baseTime = 2000; // base display time\n\n// Calculate timeout duration\nvar timeoutDuration = baseTime + (message.length * displayTimePerCharacter);\n\n// When a message is received, show image2 and hide image1\nmsg.payload = \"talk\"\n\n// Set a timeout to switch back\nsetTimeout(function() {\n node.send({payload:\"Armature\"});\n}, timeoutDuration); // Adjust the timeout as needed\n\nreturn msg;\n", 500 | "outputs": 1, 501 | "timeout": 0, 502 | "noerr": 0, 503 | "initialize": "", 504 | "finalize": "", 505 | "libs": [], 506 | "x": 390, 507 | "y": 1200, 508 | "wires": [ 509 | [ 510 | "f727b5d419564f82", 511 | "07c7307a8f61ad0c" 512 | ] 513 | ] 514 | }, 515 | { 516 | "id": "c1fccaddc34d3677", 517 | "type": "inject", 518 | "z": "14ce0cc2506ae0e4", 519 | "name": "", 520 | "props": [ 521 | { 522 | "p": "payload" 523 | } 524 | ], 525 | "repeat": "", 526 | "crontab": "", 527 | "once": false, 528 | "onceDelay": 0.1, 529 | "topic": "", 530 | "payload": "true", 531 | "payloadType": "bool", 532 | "x": 170, 533 | "y": 1500, 534 | "wires": [ 535 | [ 536 | "3e9b8df2b07d718e" 537 | ] 538 | ] 539 | }, 540 | { 541 | "id": "3e9b8df2b07d718e", 542 | "type": "trigger", 543 | "z": "14ce0cc2506ae0e4", 544 | "name": "", 545 | "op1": "{\"tab\":\"Nether_Portal\"}", 546 | "op2": "0", 547 | "op1type": "json", 548 | "op2type": "str", 549 | "duration": "250", 550 | "extend": false, 551 | "overrideDelay": false, 552 | "units": "ms", 553 | "reset": "", 554 | "bytopic": "all", 555 | "topic": "topic", 556 | "outputs": 1, 557 | "x": 380, 558 | "y": 1500, 559 | "wires": [ 560 | [ 561 | "58fe94da46e3b417" 562 | ] 563 | ] 564 | }, 565 | { 566 | "id": "7cacf3408a659ede", 567 | "type": "inject", 568 | "z": "14ce0cc2506ae0e4", 569 | "name": "", 570 | "props": [ 571 | { 572 | "p": "payload" 573 | }, 574 | { 575 | "p": "topic", 576 | "vt": "str" 577 | } 578 | ], 579 | "repeat": "", 580 | "crontab": "", 581 | "once": false, 582 | "onceDelay": 0.1, 583 | "topic": "", 584 | "payload": "true", 585 | "payloadType": "bool", 586 | "x": 170, 587 | "y": 1420, 588 | "wires": [ 589 | [ 590 | "2dbb17d6fab6c80f" 591 | ] 592 | ] 593 | }, 594 | { 595 | "id": "2dbb17d6fab6c80f", 596 | "type": "trigger", 597 | "z": "14ce0cc2506ae0e4", 598 | "name": "", 599 | "op1": "{\"tab\":\"Mirror\"}", 600 | "op2": "0", 601 | "op1type": "json", 602 | "op2type": "str", 603 | "duration": "250", 604 | "extend": false, 605 | "overrideDelay": false, 606 | "units": "ms", 607 | "reset": "", 608 | "bytopic": "all", 609 | "topic": "topic", 610 | "outputs": 2, 611 | "x": 380, 612 | "y": 1400, 613 | "wires": [ 614 | [ 615 | "b4e0ac48dac076c3", 616 | "12f968aefe6a0950", 617 | "708872d67c073019", 618 | "89da477fb7c644d3" 619 | ], 620 | [] 621 | ] 622 | }, 623 | { 624 | "id": "12f968aefe6a0950", 625 | "type": "change", 626 | "z": "14ce0cc2506ae0e4", 627 | "name": "", 628 | "rules": [ 629 | { 630 | "t": "set", 631 | "p": "reset", 632 | "pt": "msg", 633 | "to": "true", 634 | "tot": "bool" 635 | } 636 | ], 637 | "action": "", 638 | "property": "", 639 | "from": "", 640 | "to": "", 641 | "reg": false, 642 | "x": 600, 643 | "y": 1320, 644 | "wires": [ 645 | [ 646 | "ed1fc54b53cb9c4b", 647 | "d508f5f052860b04" 648 | ] 649 | ] 650 | }, 651 | { 652 | "id": "bf78823f8f35dec8", 653 | "type": "mqtt in", 654 | "z": "14ce0cc2506ae0e4", 655 | "name": "", 656 | "topic": "/wakeup", 657 | "qos": "2", 658 | "datatype": "auto-detect", 659 | "broker": "592452ad925527a7", 660 | "nl": false, 661 | "rap": true, 662 | "rh": 0, 663 | "inputs": 0, 664 | "x": 160, 665 | "y": 1340, 666 | "wires": [ 667 | [ 668 | "2dbb17d6fab6c80f", 669 | "00ca606b776bcbe3" 670 | ] 671 | ] 672 | }, 673 | { 674 | "id": "00ca606b776bcbe3", 675 | "type": "debug", 676 | "z": "14ce0cc2506ae0e4", 677 | "name": "debug 23", 678 | "active": false, 679 | "tosidebar": true, 680 | "console": false, 681 | "tostatus": false, 682 | "complete": "false", 683 | "statusVal": "", 684 | "statusType": "auto", 685 | "x": 360, 686 | "y": 1280, 687 | "wires": [] 688 | }, 689 | { 690 | "id": "d4da5fd504d1ebe5", 691 | "type": "debug", 692 | "z": "14ce0cc2506ae0e4", 693 | "name": "debug 24", 694 | "active": false, 695 | "tosidebar": true, 696 | "console": false, 697 | "tostatus": false, 698 | "complete": "false", 699 | "statusVal": "", 700 | "statusType": "auto", 701 | "x": 1040, 702 | "y": 1280, 703 | "wires": [] 704 | }, 705 | { 706 | "id": "708872d67c073019", 707 | "type": "debug", 708 | "z": "14ce0cc2506ae0e4", 709 | "name": "debug 25", 710 | "active": false, 711 | "tosidebar": true, 712 | "console": false, 713 | "tostatus": false, 714 | "complete": "false", 715 | "statusVal": "", 716 | "statusType": "auto", 717 | "x": 800, 718 | "y": 1420, 719 | "wires": [] 720 | }, 721 | { 722 | "id": "d508f5f052860b04", 723 | "type": "debug", 724 | "z": "14ce0cc2506ae0e4", 725 | "name": "debug 26", 726 | "active": false, 727 | "tosidebar": true, 728 | "console": false, 729 | "tostatus": false, 730 | "complete": "false", 731 | "statusVal": "", 732 | "statusType": "auto", 733 | "x": 800, 734 | "y": 1360, 735 | "wires": [] 736 | }, 737 | { 738 | "id": "89da477fb7c644d3", 739 | "type": "change", 740 | "z": "14ce0cc2506ae0e4", 741 | "name": "", 742 | "rules": [ 743 | { 744 | "t": "set", 745 | "p": "payload", 746 | "pt": "msg", 747 | "to": "Armature", 748 | "tot": "str" 749 | } 750 | ], 751 | "action": "", 752 | "property": "", 753 | "from": "", 754 | "to": "", 755 | "reg": false, 756 | "x": 800, 757 | "y": 1280, 758 | "wires": [ 759 | [ 760 | "f727b5d419564f82", 761 | "d4da5fd504d1ebe5" 762 | ] 763 | ] 764 | }, 765 | { 766 | "id": "07c7307a8f61ad0c", 767 | "type": "debug", 768 | "z": "14ce0cc2506ae0e4", 769 | "name": "debug 27", 770 | "active": true, 771 | "tosidebar": true, 772 | "console": false, 773 | "tostatus": false, 774 | "complete": "false", 775 | "statusVal": "", 776 | "statusType": "auto", 777 | "x": 580, 778 | "y": 1240, 779 | "wires": [] 780 | }, 781 | { 782 | "id": "5abaa4923cc1af41", 783 | "type": "inject", 784 | "z": "14ce0cc2506ae0e4", 785 | "name": "", 786 | "props": [ 787 | { 788 | "p": "payload" 789 | }, 790 | { 791 | "p": "topic", 792 | "vt": "str" 793 | } 794 | ], 795 | "repeat": "", 796 | "crontab": "00 06 * * *", 797 | "once": true, 798 | "onceDelay": "", 799 | "topic": "", 800 | "payload": "", 801 | "payloadType": "str", 802 | "x": 170, 803 | "y": 1700, 804 | "wires": [ 805 | [ 806 | "1530edf19bf5ada4" 807 | ] 808 | ] 809 | }, 810 | { 811 | "id": "4005b28432d281c4", 812 | "type": "debug", 813 | "z": "14ce0cc2506ae0e4", 814 | "name": "qod-debug", 815 | "active": false, 816 | "tosidebar": true, 817 | "console": false, 818 | "tostatus": false, 819 | "complete": "true", 820 | "targetType": "full", 821 | "x": 570, 822 | "y": 1780, 823 | "wires": [] 824 | }, 825 | { 826 | "id": "1530edf19bf5ada4", 827 | "type": "http request", 828 | "z": "14ce0cc2506ae0e4", 829 | "name": "Quote of the day", 830 | "method": "GET", 831 | "ret": "obj", 832 | "paytoqs": "ignore", 833 | "url": "https://favqs.com/api/qotd", 834 | "tls": "", 835 | "persist": false, 836 | "proxy": "", 837 | "insecureHTTPParser": false, 838 | "authType": "", 839 | "senderr": false, 840 | "headers": [], 841 | "x": 350, 842 | "y": 1700, 843 | "wires": [ 844 | [ 845 | "4005b28432d281c4", 846 | "655c287b9aea1fea" 847 | ] 848 | ] 849 | }, 850 | { 851 | "id": "655c287b9aea1fea", 852 | "type": "function", 853 | "z": "14ce0cc2506ae0e4", 854 | "name": "function 16", 855 | "func": "var data = msg.payload.quote;\nmsg.payload = {\n quote: data.body,\n author: data.author\n};\nreturn msg;\n", 856 | "outputs": 1, 857 | "timeout": 0, 858 | "noerr": 0, 859 | "initialize": "", 860 | "finalize": "", 861 | "libs": [], 862 | "x": 570, 863 | "y": 1700, 864 | "wires": [ 865 | [ 866 | "9a4aa739dfbd0427" 867 | ] 868 | ] 869 | }, 870 | { 871 | "id": "d21208f2d71460e1", 872 | "type": "mqtt in", 873 | "z": "14ce0cc2506ae0e4", 874 | "name": "", 875 | "topic": "sscma/v0/grove_vision_ai_v2_cdbef6de/tx", 876 | "qos": "2", 877 | "datatype": "auto-detect", 878 | "broker": "592452ad925527a7", 879 | "nl": false, 880 | "rap": true, 881 | "rh": 0, 882 | "inputs": 0, 883 | "x": 260, 884 | "y": 1980, 885 | "wires": [ 886 | [ 887 | "ad011d66a361a6a3", 888 | "6d992801ce7b7b68" 889 | ] 890 | ] 891 | }, 892 | { 893 | "id": "ad011d66a361a6a3", 894 | "type": "debug", 895 | "z": "14ce0cc2506ae0e4", 896 | "name": "debug 28", 897 | "active": true, 898 | "tosidebar": true, 899 | "console": false, 900 | "tostatus": false, 901 | "complete": "false", 902 | "statusVal": "", 903 | "statusType": "auto", 904 | "x": 460, 905 | "y": 1880, 906 | "wires": [] 907 | }, 908 | { 909 | "id": "791449e7c4836a33", 910 | "type": "mqtt out", 911 | "z": "14ce0cc2506ae0e4", 912 | "name": "", 913 | "topic": "sscma/v0/grove_vision_ai_v2_cdbef6de/rx", 914 | "qos": "", 915 | "retain": "", 916 | "respTopic": "", 917 | "contentType": "", 918 | "userProps": "", 919 | "correl": "", 920 | "expiry": "", 921 | "broker": "592452ad925527a7", 922 | "x": 550, 923 | "y": 2140, 924 | "wires": [] 925 | }, 926 | { 927 | "id": "9fe2985c7cfe702a", 928 | "type": "inject", 929 | "z": "14ce0cc2506ae0e4", 930 | "name": "", 931 | "props": [ 932 | { 933 | "p": "payload" 934 | } 935 | ], 936 | "repeat": "", 937 | "crontab": "", 938 | "once": false, 939 | "onceDelay": 0.1, 940 | "topic": "", 941 | "payload": "[65,84,43,73,78,86,79,75,69,61,45,49,44,48,44,48,13]", 942 | "payloadType": "bin", 943 | "x": 230, 944 | "y": 2140, 945 | "wires": [ 946 | [ 947 | "791449e7c4836a33", 948 | "29be122476b8b909" 949 | ] 950 | ] 951 | }, 952 | { 953 | "id": "6d992801ce7b7b68", 954 | "type": "function", 955 | "z": "14ce0cc2506ae0e4", 956 | "name": "function 17", 957 | "func": "// Example function node code to extract base64 data\nlet base64ImageData = msg.payload.data.image;\nmsg.payload = base64ImageData;\nreturn msg;\n", 958 | "outputs": 1, 959 | "timeout": 0, 960 | "noerr": 0, 961 | "initialize": "", 962 | "finalize": "", 963 | "libs": [], 964 | "x": 570, 965 | "y": 1980, 966 | "wires": [ 967 | [ 968 | "9786a32ec65868ea" 969 | ] 970 | ] 971 | }, 972 | { 973 | "id": "9786a32ec65868ea", 974 | "type": "debug", 975 | "z": "14ce0cc2506ae0e4", 976 | "name": "debug 29", 977 | "active": false, 978 | "tosidebar": true, 979 | "console": false, 980 | "tostatus": false, 981 | "complete": "false", 982 | "statusVal": "", 983 | "statusType": "auto", 984 | "x": 780, 985 | "y": 1940, 986 | "wires": [] 987 | }, 988 | { 989 | "id": "29be122476b8b909", 990 | "type": "debug", 991 | "z": "14ce0cc2506ae0e4", 992 | "name": "debug 30", 993 | "active": true, 994 | "tosidebar": true, 995 | "console": false, 996 | "tostatus": false, 997 | "complete": "false", 998 | "statusVal": "", 999 | "statusType": "auto", 1000 | "x": 460, 1001 | "y": 2200, 1002 | "wires": [] 1003 | }, 1004 | { 1005 | "id": "102f02fb8d7edae2", 1006 | "type": "ui_button", 1007 | "z": "14ce0cc2506ae0e4", 1008 | "name": "IconRefresh", 1009 | "group": "587f3511e9470eab", 1010 | "order": 42, 1011 | "width": 2, 1012 | "height": 2, 1013 | "passthru": false, 1014 | "label": "", 1015 | "tooltip": "Refresh", 1016 | "color": "black", 1017 | "bgcolor": "black", 1018 | "className": "", 1019 | "icon": "fa-refresh fa-4x", 1020 | "payload": "true", 1021 | "payloadType": "bool", 1022 | "topic": "", 1023 | "topicType": "str", 1024 | "x": 1070, 1025 | "y": 840, 1026 | "wires": [ 1027 | [ 1028 | "2001a88d4e694515" 1029 | ] 1030 | ] 1031 | }, 1032 | { 1033 | "id": "04807be66bc8f274", 1034 | "type": "ui_text", 1035 | "z": "14ce0cc2506ae0e4", 1036 | "group": "587f3511e9470eab", 1037 | "order": 35, 1038 | "width": 4, 1039 | "height": 1, 1040 | "name": "", 1041 | "label": "Air Humidity", 1042 | "format": "{{msg.payload}}", 1043 | "layout": "row-spread", 1044 | "className": "", 1045 | "style": false, 1046 | "font": "", 1047 | "fontSize": 16, 1048 | "color": "#000000", 1049 | "x": 910, 1050 | "y": 280, 1051 | "wires": [] 1052 | }, 1053 | { 1054 | "id": "1d234d698cd44974", 1055 | "type": "ui_text", 1056 | "z": "14ce0cc2506ae0e4", 1057 | "group": "587f3511e9470eab", 1058 | "order": 29, 1059 | "width": 4, 1060 | "height": 1, 1061 | "name": "", 1062 | "label": "Air Temperature", 1063 | "format": "{{msg.payload}}", 1064 | "layout": "row-spread", 1065 | "className": "", 1066 | "style": false, 1067 | "font": "", 1068 | "fontSize": 16, 1069 | "color": "#000000", 1070 | "x": 920, 1071 | "y": 340, 1072 | "wires": [] 1073 | }, 1074 | { 1075 | "id": "8bb25af424b79629", 1076 | "type": "ui_text", 1077 | "z": "14ce0cc2506ae0e4", 1078 | "group": "587f3511e9470eab", 1079 | "order": 32, 1080 | "width": 4, 1081 | "height": 1, 1082 | "name": "", 1083 | "label": "CO2", 1084 | "format": "{{msg.payload}}", 1085 | "layout": "row-spread", 1086 | "className": "", 1087 | "style": false, 1088 | "font": "", 1089 | "fontSize": 16, 1090 | "color": "#000000", 1091 | "x": 890, 1092 | "y": 400, 1093 | "wires": [] 1094 | }, 1095 | { 1096 | "id": "7d8167469cc6011f", 1097 | "type": "ui_text", 1098 | "z": "14ce0cc2506ae0e4", 1099 | "group": "587f3511e9470eab", 1100 | "order": 5, 1101 | "width": 4, 1102 | "height": 1, 1103 | "name": "Description", 1104 | "label": "", 1105 | "format": "{{msg.payload.current.weather[0].description}}", 1106 | "layout": "row-right", 1107 | "className": "", 1108 | "style": true, 1109 | "font": "Arial,Arial,Helvetica,sans-serif", 1110 | "fontSize": "16", 1111 | "color": "#ffffff", 1112 | "x": 1070, 1113 | "y": 680, 1114 | "wires": [] 1115 | }, 1116 | { 1117 | "id": "6aa891ced7bdbae9", 1118 | "type": "ui_text", 1119 | "z": "14ce0cc2506ae0e4", 1120 | "group": "587f3511e9470eab", 1121 | "order": 7, 1122 | "width": 4, 1123 | "height": 1, 1124 | "name": "Wind", 1125 | "label": "", 1126 | "format": "{{msg.payload.current.wind_speed}}    {{msg.payload.current.wind_cardinal}}", 1127 | "layout": "col-center", 1128 | "className": "", 1129 | "style": true, 1130 | "font": "Arial Narrow,Nimbus Sans L,sans-serif", 1131 | "fontSize": "25", 1132 | "color": "#77767b", 1133 | "x": 1050, 1134 | "y": 720, 1135 | "wires": [] 1136 | }, 1137 | { 1138 | "id": "54f9d1e8673e7632", 1139 | "type": "ui_text", 1140 | "z": "14ce0cc2506ae0e4", 1141 | "group": "587f3511e9470eab", 1142 | "order": 13, 1143 | "width": 4, 1144 | "height": 1, 1145 | "name": "SunriseTime", 1146 | "label": "", 1147 | "format": "   {{msg.payload.current.sunrise}}", 1148 | "layout": "row-right", 1149 | "className": "", 1150 | "style": false, 1151 | "font": "", 1152 | "fontSize": "", 1153 | "color": "#000000", 1154 | "x": 1070, 1155 | "y": 760, 1156 | "wires": [] 1157 | }, 1158 | { 1159 | "id": "406acc32b466c5ca", 1160 | "type": "ui_text", 1161 | "z": "14ce0cc2506ae0e4", 1162 | "group": "587f3511e9470eab", 1163 | "order": 14, 1164 | "width": 4, 1165 | "height": 1, 1166 | "name": "SunsetTime", 1167 | "label": "", 1168 | "format": "   {{msg.payload.current.sunset}}", 1169 | "layout": "row-right", 1170 | "className": "", 1171 | "style": false, 1172 | "font": "", 1173 | "fontSize": "", 1174 | "color": "#000000", 1175 | "x": 1070, 1176 | "y": 800, 1177 | "wires": [] 1178 | }, 1179 | { 1180 | "id": "cf61925f71764024", 1181 | "type": "ui_text", 1182 | "z": "14ce0cc2506ae0e4", 1183 | "group": "587f3511e9470eab", 1184 | "order": 4, 1185 | "width": 4, 1186 | "height": 2, 1187 | "name": "Temperature", 1188 | "label": "", 1189 | "format": "{{msg.payload.current.temp}}", 1190 | "layout": "row-right", 1191 | "className": "", 1192 | "style": true, 1193 | "font": "Arial Narrow,Nimbus Sans L,sans-serif", 1194 | "fontSize": "60", 1195 | "color": "#ffffff", 1196 | "x": 1070, 1197 | "y": 640, 1198 | "wires": [] 1199 | }, 1200 | { 1201 | "id": "73d31b2869c35344", 1202 | "type": "ui_text", 1203 | "z": "14ce0cc2506ae0e4", 1204 | "group": "587f3511e9470eab", 1205 | "order": 2, 1206 | "width": 10, 1207 | "height": 2, 1208 | "name": "System Time", 1209 | "label": "", 1210 | "format": "{{msg.payload}}", 1211 | "layout": "row-left", 1212 | "className": "", 1213 | "style": true, 1214 | "font": "Arial Narrow,Nimbus Sans L,sans-serif", 1215 | "fontSize": "60", 1216 | "color": "#ffffff", 1217 | "x": 910, 1218 | "y": 160, 1219 | "wires": [] 1220 | }, 1221 | { 1222 | "id": "37983d4eb44685bf", 1223 | "type": "ui_text", 1224 | "z": "14ce0cc2506ae0e4", 1225 | "group": "587f3511e9470eab", 1226 | "order": 8, 1227 | "width": 11, 1228 | "height": 1, 1229 | "name": "Local Date", 1230 | "label": "", 1231 | "format": "{{msg.payload}}", 1232 | "layout": "row-left", 1233 | "className": "", 1234 | "style": true, 1235 | "font": "Arial Narrow,Nimbus Sans L,sans-serif", 1236 | "fontSize": "25", 1237 | "color": "#77767b", 1238 | "x": 910, 1239 | "y": 200, 1240 | "wires": [] 1241 | }, 1242 | { 1243 | "id": "0185ad28c3a1642e", 1244 | "type": "ui_text", 1245 | "z": "14ce0cc2506ae0e4", 1246 | "d": true, 1247 | "group": "d783cb980c3979fc", 1248 | "order": 34, 1249 | "width": 4, 1250 | "height": 1, 1251 | "name": "Light Switch", 1252 | "label": "Light Switch", 1253 | "format": "{{msg.payload}}", 1254 | "layout": "row-spread", 1255 | "className": "", 1256 | "style": false, 1257 | "font": "", 1258 | "fontSize": 16, 1259 | "color": "#000000", 1260 | "x": 910, 1261 | "y": 460, 1262 | "wires": [] 1263 | }, 1264 | { 1265 | "id": "85b8132805e53c39", 1266 | "type": "ui_ui_control", 1267 | "z": "14ce0cc2506ae0e4", 1268 | "name": "Update tab", 1269 | "events": "all", 1270 | "x": 150, 1271 | "y": 760, 1272 | "wires": [ 1273 | [ 1274 | "6afcc93d37c8a315" 1275 | ] 1276 | ] 1277 | }, 1278 | { 1279 | "id": "1cdedf4bf06e7f7f", 1280 | "type": "ui_ui_control", 1281 | "z": "14ce0cc2506ae0e4", 1282 | "name": "", 1283 | "events": "all", 1284 | "x": 860, 1285 | "y": 960, 1286 | "wires": [ 1287 | [ 1288 | "102f02fb8d7edae2" 1289 | ] 1290 | ] 1291 | }, 1292 | { 1293 | "id": "58fe94da46e3b417", 1294 | "type": "ui_ui_control", 1295 | "z": "14ce0cc2506ae0e4", 1296 | "name": "", 1297 | "events": "change", 1298 | "x": 600, 1299 | "y": 1500, 1300 | "wires": [ 1301 | [] 1302 | ] 1303 | }, 1304 | { 1305 | "id": "b4e0ac48dac076c3", 1306 | "type": "ui_ui_control", 1307 | "z": "14ce0cc2506ae0e4", 1308 | "name": "", 1309 | "events": "change", 1310 | "x": 600, 1311 | "y": 1420, 1312 | "wires": [ 1313 | [] 1314 | ] 1315 | }, 1316 | { 1317 | "id": "2eba8e74a0ded4f5", 1318 | "type": "ui_template", 1319 | "z": "14ce0cc2506ae0e4", 1320 | "group": "587f3511e9470eab", 1321 | "name": "Forecast2", 1322 | "order": 17, 1323 | "width": 7, 1324 | "height": 2, 1325 | "format": "
\n\n
\n \n \n \n \n \n
\n
\n {{data.cell01}}\n {{data.cell02}}\n {{data.cell03}}\n {{data.cell04}}\n
\n
", 1326 | "storeOutMessages": true, 1327 | "fwdInMessages": true, 1328 | "resendOnRefresh": false, 1329 | "templateScope": "local", 1330 | "className": "", 1331 | "x": 1060, 1332 | "y": 900, 1333 | "wires": [ 1334 | [] 1335 | ] 1336 | }, 1337 | { 1338 | "id": "30ecbb8c13003025", 1339 | "type": "ui_template", 1340 | "z": "14ce0cc2506ae0e4", 1341 | "group": "587f3511e9470eab", 1342 | "name": "", 1343 | "order": 40, 1344 | "width": 1, 1345 | "height": 1, 1346 | "format": "", 1347 | "storeOutMessages": true, 1348 | "fwdInMessages": true, 1349 | "resendOnRefresh": true, 1350 | "templateScope": "local", 1351 | "className": "", 1352 | "x": 140, 1353 | "y": 40, 1354 | "wires": [ 1355 | [] 1356 | ] 1357 | }, 1358 | { 1359 | "id": "e14a631c1ace4905", 1360 | "type": "ui_template", 1361 | "z": "14ce0cc2506ae0e4", 1362 | "group": "587f3511e9470eab", 1363 | "name": "Forecast3", 1364 | "order": 20, 1365 | "width": 7, 1366 | "height": 2, 1367 | "format": "
\n
\n \n \n \n \n
\n
\n {{data.cell07}}\n {{data.cell08}}\n {{data.cell09}}\n {{data.cell10}}\n
\n
", 1368 | "storeOutMessages": true, 1369 | "fwdInMessages": true, 1370 | "resendOnRefresh": false, 1371 | "templateScope": "local", 1372 | "className": "", 1373 | "x": 1060, 1374 | "y": 940, 1375 | "wires": [ 1376 | [] 1377 | ] 1378 | }, 1379 | { 1380 | "id": "0afbe505e0be0868", 1381 | "type": "ui_template", 1382 | "z": "14ce0cc2506ae0e4", 1383 | "group": "587f3511e9470eab", 1384 | "name": "Weather Name", 1385 | "order": 10, 1386 | "width": 8, 1387 | "height": 1, 1388 | "format": "
WEATHER FORCAST SHENZHEN, CN
\n", 1389 | "storeOutMessages": true, 1390 | "fwdInMessages": true, 1391 | "resendOnRefresh": true, 1392 | "templateScope": "local", 1393 | "className": "", 1394 | "x": 1080, 1395 | "y": 980, 1396 | "wires": [ 1397 | [] 1398 | ] 1399 | }, 1400 | { 1401 | "id": "e4e17ca24325f47b", 1402 | "type": "ui_template", 1403 | "z": "14ce0cc2506ae0e4", 1404 | "group": "587f3511e9470eab", 1405 | "name": "Office Assistant", 1406 | "order": 24, 1407 | "width": 4, 1408 | "height": 1, 1409 | "format": "
Office Assistant
\n", 1410 | "storeOutMessages": true, 1411 | "fwdInMessages": true, 1412 | "resendOnRefresh": true, 1413 | "templateScope": "local", 1414 | "className": "", 1415 | "x": 900, 1416 | "y": 240, 1417 | "wires": [ 1418 | [] 1419 | ] 1420 | }, 1421 | { 1422 | "id": "c863b9b6e5153681", 1423 | "type": "ui_template", 1424 | "z": "14ce0cc2506ae0e4", 1425 | "group": "587f3511e9470eab", 1426 | "name": "conversion 3", 1427 | "order": 59, 1428 | "width": 16, 1429 | "height": 6, 1430 | "format": "
\n
{{ message.name }} - {{ message.timestamp }}
\n
\n
\n\n\n\n", 1431 | "storeOutMessages": true, 1432 | "fwdInMessages": true, 1433 | "resendOnRefresh": true, 1434 | "templateScope": "local", 1435 | "className": "", 1436 | "x": 830, 1437 | "y": 1140, 1438 | "wires": [ 1439 | [ 1440 | "8347b875552b29f2" 1441 | ] 1442 | ] 1443 | }, 1444 | { 1445 | "id": "f727b5d419564f82", 1446 | "type": "ui_template", 1447 | "z": "14ce0cc2506ae0e4", 1448 | "group": "587f3511e9470eab", 1449 | "name": "3d model", 1450 | "order": 22, 1451 | "width": 10, 1452 | "height": 13, 1453 | "format": "\n\n\n", 1454 | "storeOutMessages": true, 1455 | "fwdInMessages": true, 1456 | "resendOnRefresh": true, 1457 | "templateScope": "local", 1458 | "className": "", 1459 | "x": 820, 1460 | "y": 1200, 1461 | "wires": [ 1462 | [] 1463 | ] 1464 | }, 1465 | { 1466 | "id": "7568d027dcee34ba", 1467 | "type": "ui_template", 1468 | "z": "14ce0cc2506ae0e4", 1469 | "group": "0bbad19b45c1397b", 1470 | "name": "nether-portal", 1471 | "order": 1, 1472 | "width": 0, 1473 | "height": 0, 1474 | "format": "\n", 1475 | "storeOutMessages": true, 1476 | "fwdInMessages": true, 1477 | "resendOnRefresh": true, 1478 | "templateScope": "local", 1479 | "className": "", 1480 | "x": 770, 1481 | "y": 1560, 1482 | "wires": [ 1483 | [] 1484 | ] 1485 | }, 1486 | { 1487 | "id": "9a4aa739dfbd0427", 1488 | "type": "ui_template", 1489 | "z": "14ce0cc2506ae0e4", 1490 | "group": "587f3511e9470eab", 1491 | "name": "", 1492 | "order": 72, 1493 | "width": 16, 1494 | "height": 5, 1495 | "format": "
\n
{{msg.payload.quote}}
\n

- {{msg.payload.author}}

\n
\n\n", 1496 | "storeOutMessages": true, 1497 | "fwdInMessages": true, 1498 | "resendOnRefresh": true, 1499 | "templateScope": "local", 1500 | "className": "", 1501 | "x": 760, 1502 | "y": 1700, 1503 | "wires": [ 1504 | [] 1505 | ] 1506 | }, 1507 | { 1508 | "id": "4db931f60417ec48", 1509 | "type": "ui_template", 1510 | "z": "14ce0cc2506ae0e4", 1511 | "d": true, 1512 | "group": "d783cb980c3979fc", 1513 | "name": "CAM", 1514 | "order": 139, 1515 | "width": 5, 1516 | "height": 4, 1517 | "format": "\n\n
\n
\n \"image\"\n
\n
", 1518 | "storeOutMessages": true, 1519 | "fwdInMessages": true, 1520 | "resendOnRefresh": true, 1521 | "templateScope": "local", 1522 | "className": "", 1523 | "x": 770, 1524 | "y": 2000, 1525 | "wires": [ 1526 | [] 1527 | ] 1528 | }, 1529 | { 1530 | "id": "193bde23c05c9541", 1531 | "type": "ui_template", 1532 | "z": "14ce0cc2506ae0e4", 1533 | "group": "587f3511e9470eab", 1534 | "name": "Grove Vision AI CAM", 1535 | "order": 11, 1536 | "width": 4, 1537 | "height": 1, 1538 | "format": "
Grove Vision AI CAM
\n", 1539 | "storeOutMessages": true, 1540 | "fwdInMessages": true, 1541 | "resendOnRefresh": true, 1542 | "templateScope": "local", 1543 | "className": "", 1544 | "x": 200, 1545 | "y": 1780, 1546 | "wires": [ 1547 | [] 1548 | ] 1549 | }, 1550 | { 1551 | "id": "3780445e34a7c5d1", 1552 | "type": "api-current-state", 1553 | "z": "14ce0cc2506ae0e4", 1554 | "name": "", 1555 | "server": "48815cdd5546b75b", 1556 | "version": 3, 1557 | "outputs": 1, 1558 | "halt_if": "", 1559 | "halt_if_type": "str", 1560 | "halt_if_compare": "is", 1561 | "entity_id": "sensor.2cf7f1c053300104_air_humidity", 1562 | "state_type": "str", 1563 | "blockInputOverrides": false, 1564 | "outputProperties": [ 1565 | { 1566 | "property": "payload", 1567 | "propertyType": "msg", 1568 | "value": "", 1569 | "valueType": "entityState" 1570 | }, 1571 | { 1572 | "property": "data", 1573 | "propertyType": "msg", 1574 | "value": "", 1575 | "valueType": "entity" 1576 | } 1577 | ], 1578 | "for": "0", 1579 | "forType": "num", 1580 | "forUnits": "minutes", 1581 | "override_topic": false, 1582 | "state_location": "payload", 1583 | "override_payload": "msg", 1584 | "entity_location": "data", 1585 | "override_data": "msg", 1586 | "x": 560, 1587 | "y": 280, 1588 | "wires": [ 1589 | [ 1590 | "04807be66bc8f274" 1591 | ] 1592 | ] 1593 | }, 1594 | { 1595 | "id": "f93a18da2118731a", 1596 | "type": "api-current-state", 1597 | "z": "14ce0cc2506ae0e4", 1598 | "name": "", 1599 | "server": "48815cdd5546b75b", 1600 | "version": 3, 1601 | "outputs": 1, 1602 | "halt_if": "", 1603 | "halt_if_type": "str", 1604 | "halt_if_compare": "is", 1605 | "entity_id": "sensor.2cf7f1c053300104_air_temperature", 1606 | "state_type": "str", 1607 | "blockInputOverrides": false, 1608 | "outputProperties": [ 1609 | { 1610 | "property": "payload", 1611 | "propertyType": "msg", 1612 | "value": "", 1613 | "valueType": "entityState" 1614 | }, 1615 | { 1616 | "property": "data", 1617 | "propertyType": "msg", 1618 | "value": "", 1619 | "valueType": "entity" 1620 | } 1621 | ], 1622 | "for": "0", 1623 | "forType": "num", 1624 | "forUnits": "minutes", 1625 | "override_topic": false, 1626 | "state_location": "payload", 1627 | "override_payload": "msg", 1628 | "entity_location": "data", 1629 | "override_data": "msg", 1630 | "x": 570, 1631 | "y": 340, 1632 | "wires": [ 1633 | [ 1634 | "1d234d698cd44974" 1635 | ] 1636 | ] 1637 | }, 1638 | { 1639 | "id": "1d7ce67c0a72f6c8", 1640 | "type": "api-current-state", 1641 | "z": "14ce0cc2506ae0e4", 1642 | "name": "", 1643 | "server": "48815cdd5546b75b", 1644 | "version": 3, 1645 | "outputs": 1, 1646 | "halt_if": "", 1647 | "halt_if_type": "str", 1648 | "halt_if_compare": "is", 1649 | "entity_id": "sensor.2cf7f1c053300104_co2", 1650 | "state_type": "str", 1651 | "blockInputOverrides": false, 1652 | "outputProperties": [ 1653 | { 1654 | "property": "payload", 1655 | "propertyType": "msg", 1656 | "value": "", 1657 | "valueType": "entityState" 1658 | }, 1659 | { 1660 | "property": "data", 1661 | "propertyType": "msg", 1662 | "value": "", 1663 | "valueType": "entity" 1664 | } 1665 | ], 1666 | "for": "0", 1667 | "forType": "num", 1668 | "forUnits": "minutes", 1669 | "override_topic": false, 1670 | "state_location": "payload", 1671 | "override_payload": "msg", 1672 | "entity_location": "data", 1673 | "override_data": "msg", 1674 | "x": 530, 1675 | "y": 400, 1676 | "wires": [ 1677 | [ 1678 | "8bb25af424b79629" 1679 | ] 1680 | ] 1681 | }, 1682 | { 1683 | "id": "d1c0578545c348fb", 1684 | "type": "api-current-state", 1685 | "z": "14ce0cc2506ae0e4", 1686 | "name": "", 1687 | "server": "48815cdd5546b75b", 1688 | "version": 3, 1689 | "outputs": 1, 1690 | "halt_if": "", 1691 | "halt_if_type": "str", 1692 | "halt_if_compare": "is", 1693 | "entity_id": "switch.100179cfeb", 1694 | "state_type": "str", 1695 | "blockInputOverrides": false, 1696 | "outputProperties": [ 1697 | { 1698 | "property": "payload", 1699 | "propertyType": "msg", 1700 | "value": "", 1701 | "valueType": "entityState" 1702 | }, 1703 | { 1704 | "property": "data", 1705 | "propertyType": "msg", 1706 | "value": "", 1707 | "valueType": "entity" 1708 | } 1709 | ], 1710 | "for": "0", 1711 | "forType": "num", 1712 | "forUnits": "minutes", 1713 | "override_topic": false, 1714 | "state_location": "payload", 1715 | "override_payload": "msg", 1716 | "entity_location": "data", 1717 | "override_data": "msg", 1718 | "x": 520, 1719 | "y": 460, 1720 | "wires": [ 1721 | [ 1722 | "0185ad28c3a1642e" 1723 | ] 1724 | ] 1725 | }, 1726 | { 1727 | "id": "ded683cb3f621ec9", 1728 | "type": "inject", 1729 | "z": "14ce0cc2506ae0e4", 1730 | "name": "", 1731 | "props": [ 1732 | { 1733 | "p": "payload" 1734 | }, 1735 | { 1736 | "p": "topic", 1737 | "vt": "str" 1738 | } 1739 | ], 1740 | "repeat": "", 1741 | "crontab": "", 1742 | "once": false, 1743 | "onceDelay": 0.1, 1744 | "topic": "", 1745 | "payload": "true", 1746 | "payloadType": "bool", 1747 | "x": 170, 1748 | "y": 1100, 1749 | "wires": [ 1750 | [ 1751 | "4a22517b04b5caf6" 1752 | ] 1753 | ] 1754 | }, 1755 | { 1756 | "id": "02302aca38bb28bf", 1757 | "type": "inject", 1758 | "z": "14ce0cc2506ae0e4", 1759 | "name": "", 1760 | "props": [ 1761 | { 1762 | "p": "payload" 1763 | }, 1764 | { 1765 | "p": "topic", 1766 | "vt": "str" 1767 | } 1768 | ], 1769 | "repeat": "", 1770 | "crontab": "", 1771 | "once": false, 1772 | "onceDelay": 0.1, 1773 | "topic": "", 1774 | "payload": "hi, arya i am a virtual assisatant", 1775 | "payloadType": "str", 1776 | "x": 150, 1777 | "y": 1280, 1778 | "wires": [ 1779 | [ 1780 | "4a22517b04b5caf6" 1781 | ] 1782 | ] 1783 | }, 1784 | { 1785 | "id": "7ffa048edbdc4ff5", 1786 | "type": "ui_spacer", 1787 | "z": "14ce0cc2506ae0e4", 1788 | "name": "spacer", 1789 | "group": "567c51cd17e47797", 1790 | "order": 1, 1791 | "width": 13, 1792 | "height": 1 1793 | }, 1794 | { 1795 | "id": "ab1cb9f09b6b6142", 1796 | "type": "ui_spacer", 1797 | "z": "14ce0cc2506ae0e4", 1798 | "name": "spacer", 1799 | "group": "567c51cd17e47797", 1800 | "order": 3, 1801 | "width": 3, 1802 | "height": 1 1803 | }, 1804 | { 1805 | "id": "96cc3e7b2e1b47dc", 1806 | "type": "ui_spacer", 1807 | "z": "14ce0cc2506ae0e4", 1808 | "name": "spacer", 1809 | "group": "567c51cd17e47797", 1810 | "order": 5, 1811 | "width": 5, 1812 | "height": 1 1813 | }, 1814 | { 1815 | "id": "1345ebb80f4c9f6d", 1816 | "type": "ui_spacer", 1817 | "z": "14ce0cc2506ae0e4", 1818 | "name": "spacer", 1819 | "group": "567c51cd17e47797", 1820 | "order": 8, 1821 | "width": 6, 1822 | "height": 1 1823 | }, 1824 | { 1825 | "id": "210f422b166661de", 1826 | "type": "ui_spacer", 1827 | "z": "14ce0cc2506ae0e4", 1828 | "name": "spacer", 1829 | "group": "567c51cd17e47797", 1830 | "order": 10, 1831 | "width": 6, 1832 | "height": 1 1833 | }, 1834 | { 1835 | "id": "5be22113e850a92f", 1836 | "type": "ui_spacer", 1837 | "z": "14ce0cc2506ae0e4", 1838 | "name": "spacer", 1839 | "group": "567c51cd17e47797", 1840 | "order": 12, 1841 | "width": 8, 1842 | "height": 1 1843 | }, 1844 | { 1845 | "id": "0fb9ec7fc63aaca6", 1846 | "type": "ui_spacer", 1847 | "z": "14ce0cc2506ae0e4", 1848 | "name": "spacer", 1849 | "group": "567c51cd17e47797", 1850 | "order": 15, 1851 | "width": 7, 1852 | "height": 1 1853 | }, 1854 | { 1855 | "id": "050af8e1c081fa4b", 1856 | "type": "ui_spacer", 1857 | "z": "14ce0cc2506ae0e4", 1858 | "name": "spacer", 1859 | "group": "567c51cd17e47797", 1860 | "order": 18, 1861 | "width": 8, 1862 | "height": 1 1863 | }, 1864 | { 1865 | "id": "23a0d7647fa7a655", 1866 | "type": "ui_spacer", 1867 | "z": "14ce0cc2506ae0e4", 1868 | "name": "spacer", 1869 | "group": "567c51cd17e47797", 1870 | "order": 20, 1871 | "width": 8, 1872 | "height": 1 1873 | }, 1874 | { 1875 | "id": "6339bdaab0ece8db", 1876 | "type": "ui_spacer", 1877 | "z": "14ce0cc2506ae0e4", 1878 | "name": "spacer", 1879 | "group": "567c51cd17e47797", 1880 | "order": 21, 1881 | "width": 8, 1882 | "height": 1 1883 | }, 1884 | { 1885 | "id": "24df866377b1acab", 1886 | "type": "ui_spacer", 1887 | "z": "14ce0cc2506ae0e4", 1888 | "name": "spacer", 1889 | "group": "567c51cd17e47797", 1890 | "order": 23, 1891 | "width": 13, 1892 | "height": 1 1893 | }, 1894 | { 1895 | "id": "7a8e0fa161ca1443", 1896 | "type": "ui_spacer", 1897 | "z": "14ce0cc2506ae0e4", 1898 | "name": "spacer", 1899 | "group": "567c51cd17e47797", 1900 | "order": 24, 1901 | "width": 5, 1902 | "height": 1 1903 | }, 1904 | { 1905 | "id": "d7f70533abd4cc98", 1906 | "type": "ui_spacer", 1907 | "z": "14ce0cc2506ae0e4", 1908 | "name": "spacer", 1909 | "group": "567c51cd17e47797", 1910 | "order": 26, 1911 | "width": 1, 1912 | "height": 1 1913 | }, 1914 | { 1915 | "id": "efee6de5abe80cf5", 1916 | "type": "ui_spacer", 1917 | "z": "14ce0cc2506ae0e4", 1918 | "name": "spacer", 1919 | "group": "567c51cd17e47797", 1920 | "order": 28, 1921 | "width": 5, 1922 | "height": 1 1923 | }, 1924 | { 1925 | "id": "2361c9c2d534e4b4", 1926 | "type": "ui_spacer", 1927 | "z": "14ce0cc2506ae0e4", 1928 | "name": "spacer", 1929 | "group": "567c51cd17e47797", 1930 | "order": 29, 1931 | "width": 1, 1932 | "height": 1 1933 | }, 1934 | { 1935 | "id": "0d9d41f3a021e453", 1936 | "type": "ui_spacer", 1937 | "z": "14ce0cc2506ae0e4", 1938 | "name": "spacer", 1939 | "group": "567c51cd17e47797", 1940 | "order": 31, 1941 | "width": 5, 1942 | "height": 1 1943 | }, 1944 | { 1945 | "id": "3a24fd2018d73cc5", 1946 | "type": "ui_spacer", 1947 | "z": "14ce0cc2506ae0e4", 1948 | "name": "spacer", 1949 | "group": "567c51cd17e47797", 1950 | "order": 32, 1951 | "width": 1, 1952 | "height": 1 1953 | }, 1954 | { 1955 | "id": "6cc3f2d6f6d140f3", 1956 | "type": "ui_spacer", 1957 | "z": "14ce0cc2506ae0e4", 1958 | "name": "spacer", 1959 | "group": "567c51cd17e47797", 1960 | "order": 34, 1961 | "width": 5, 1962 | "height": 1 1963 | }, 1964 | { 1965 | "id": "f4c70a92b6e9ce1c", 1966 | "type": "ui_spacer", 1967 | "z": "14ce0cc2506ae0e4", 1968 | "name": "spacer", 1969 | "group": "567c51cd17e47797", 1970 | "order": 35, 1971 | "width": 1, 1972 | "height": 1 1973 | }, 1974 | { 1975 | "id": "3d1ebea9d7ab146d", 1976 | "type": "ui_spacer", 1977 | "z": "14ce0cc2506ae0e4", 1978 | "name": "spacer", 1979 | "group": "567c51cd17e47797", 1980 | "order": 37, 1981 | "width": 5, 1982 | "height": 1 1983 | }, 1984 | { 1985 | "id": "61743257b427b65f", 1986 | "type": "ui_spacer", 1987 | "z": "14ce0cc2506ae0e4", 1988 | "name": "spacer", 1989 | "group": "567c51cd17e47797", 1990 | "order": 38, 1991 | "width": 1, 1992 | "height": 1 1993 | }, 1994 | { 1995 | "id": "e51ab4594170a3fe", 1996 | "type": "ui_spacer", 1997 | "z": "14ce0cc2506ae0e4", 1998 | "name": "spacer", 1999 | "group": "567c51cd17e47797", 2000 | "order": 41, 2001 | "width": 3, 2002 | "height": 1 2003 | }, 2004 | { 2005 | "id": "e6f9a473c186bca0", 2006 | "type": "ui_spacer", 2007 | "z": "14ce0cc2506ae0e4", 2008 | "name": "spacer", 2009 | "group": "567c51cd17e47797", 2010 | "order": 42, 2011 | "width": 5, 2012 | "height": 1 2013 | }, 2014 | { 2015 | "id": "ae9a175e19db0d63", 2016 | "type": "ui_spacer", 2017 | "z": "14ce0cc2506ae0e4", 2018 | "name": "spacer", 2019 | "group": "567c51cd17e47797", 2020 | "order": 43, 2021 | "width": 3, 2022 | "height": 1 2023 | }, 2024 | { 2025 | "id": "dee8873eac6c658d", 2026 | "type": "ui_spacer", 2027 | "z": "14ce0cc2506ae0e4", 2028 | "name": "spacer", 2029 | "group": "567c51cd17e47797", 2030 | "order": 44, 2031 | "width": 5, 2032 | "height": 1 2033 | }, 2034 | { 2035 | "id": "b485e756d38aa125", 2036 | "type": "ui_spacer", 2037 | "z": "14ce0cc2506ae0e4", 2038 | "name": "spacer", 2039 | "group": "567c51cd17e47797", 2040 | "order": 46, 2041 | "width": 4, 2042 | "height": 1 2043 | }, 2044 | { 2045 | "id": "d2d0988941bd3aa2", 2046 | "type": "ui_spacer", 2047 | "z": "14ce0cc2506ae0e4", 2048 | "name": "spacer", 2049 | "group": "567c51cd17e47797", 2050 | "order": 47, 2051 | "width": 5, 2052 | "height": 1 2053 | }, 2054 | { 2055 | "id": "2073fb65a19fa5a3", 2056 | "type": "ui_spacer", 2057 | "z": "14ce0cc2506ae0e4", 2058 | "name": "spacer", 2059 | "group": "567c51cd17e47797", 2060 | "order": 48, 2061 | "width": 5, 2062 | "height": 1 2063 | }, 2064 | { 2065 | "id": "9c1a6aea260da7e6", 2066 | "type": "ui_spacer", 2067 | "z": "14ce0cc2506ae0e4", 2068 | "name": "spacer", 2069 | "group": "567c51cd17e47797", 2070 | "order": 49, 2071 | "width": 5, 2072 | "height": 1 2073 | }, 2074 | { 2075 | "id": "637ac5bdbd799131", 2076 | "type": "ui_spacer", 2077 | "z": "14ce0cc2506ae0e4", 2078 | "name": "spacer", 2079 | "group": "567c51cd17e47797", 2080 | "order": 50, 2081 | "width": 5, 2082 | "height": 1 2083 | }, 2084 | { 2085 | "id": "95e68b9f0afb1ec3", 2086 | "type": "ui_spacer", 2087 | "z": "14ce0cc2506ae0e4", 2088 | "name": "spacer", 2089 | "group": "567c51cd17e47797", 2090 | "order": 51, 2091 | "width": 5, 2092 | "height": 1 2093 | }, 2094 | { 2095 | "id": "d419f8da03bf424f", 2096 | "type": "ui_spacer", 2097 | "z": "14ce0cc2506ae0e4", 2098 | "name": "spacer", 2099 | "group": "567c51cd17e47797", 2100 | "order": 52, 2101 | "width": 5, 2102 | "height": 1 2103 | }, 2104 | { 2105 | "id": "beaaa7a519722108", 2106 | "type": "ui_spacer", 2107 | "z": "14ce0cc2506ae0e4", 2108 | "name": "spacer", 2109 | "group": "567c51cd17e47797", 2110 | "order": 53, 2111 | "width": 5, 2112 | "height": 1 2113 | }, 2114 | { 2115 | "id": "f26c17be8fb0074b", 2116 | "type": "ui_spacer", 2117 | "z": "14ce0cc2506ae0e4", 2118 | "name": "spacer", 2119 | "group": "567c51cd17e47797", 2120 | "order": 54, 2121 | "width": 5, 2122 | "height": 1 2123 | }, 2124 | { 2125 | "id": "b28e2b5e0fe54575", 2126 | "type": "ui_spacer", 2127 | "z": "14ce0cc2506ae0e4", 2128 | "name": "spacer", 2129 | "group": "567c51cd17e47797", 2130 | "order": 55, 2131 | "width": 5, 2132 | "height": 1 2133 | }, 2134 | { 2135 | "id": "0ce0675d87829384", 2136 | "type": "ui_spacer", 2137 | "z": "14ce0cc2506ae0e4", 2138 | "name": "spacer", 2139 | "group": "567c51cd17e47797", 2140 | "order": 56, 2141 | "width": 5, 2142 | "height": 1 2143 | }, 2144 | { 2145 | "id": "ef370152137ea194", 2146 | "type": "ui_spacer", 2147 | "z": "14ce0cc2506ae0e4", 2148 | "name": "spacer", 2149 | "group": "567c51cd17e47797", 2150 | "order": 57, 2151 | "width": 5, 2152 | "height": 1 2153 | }, 2154 | { 2155 | "id": "a99f968cf814bfd2", 2156 | "type": "ui_spacer", 2157 | "z": "14ce0cc2506ae0e4", 2158 | "name": "spacer", 2159 | "group": "567c51cd17e47797", 2160 | "order": 58, 2161 | "width": 2, 2162 | "height": 1 2163 | }, 2164 | { 2165 | "id": "f570ada3b747f1df", 2166 | "type": "ui_spacer", 2167 | "z": "14ce0cc2506ae0e4", 2168 | "name": "spacer", 2169 | "group": "567c51cd17e47797", 2170 | "order": 60, 2171 | "width": 2, 2172 | "height": 1 2173 | }, 2174 | { 2175 | "id": "82cfe07908aca2c0", 2176 | "type": "ui_spacer", 2177 | "z": "14ce0cc2506ae0e4", 2178 | "name": "spacer", 2179 | "group": "567c51cd17e47797", 2180 | "order": 61, 2181 | "width": 2, 2182 | "height": 1 2183 | }, 2184 | { 2185 | "id": "541f3b317f929701", 2186 | "type": "ui_spacer", 2187 | "z": "14ce0cc2506ae0e4", 2188 | "name": "spacer", 2189 | "group": "567c51cd17e47797", 2190 | "order": 62, 2191 | "width": 2, 2192 | "height": 1 2193 | }, 2194 | { 2195 | "id": "8e5d43a181125dc1", 2196 | "type": "ui_spacer", 2197 | "z": "14ce0cc2506ae0e4", 2198 | "name": "spacer", 2199 | "group": "567c51cd17e47797", 2200 | "order": 63, 2201 | "width": 2, 2202 | "height": 1 2203 | }, 2204 | { 2205 | "id": "6fb37a129bb8c916", 2206 | "type": "ui_spacer", 2207 | "z": "14ce0cc2506ae0e4", 2208 | "name": "spacer", 2209 | "group": "567c51cd17e47797", 2210 | "order": 64, 2211 | "width": 2, 2212 | "height": 1 2213 | }, 2214 | { 2215 | "id": "5565ce55e7b5e1b3", 2216 | "type": "ui_spacer", 2217 | "z": "14ce0cc2506ae0e4", 2218 | "name": "spacer", 2219 | "group": "567c51cd17e47797", 2220 | "order": 65, 2221 | "width": 2, 2222 | "height": 1 2223 | }, 2224 | { 2225 | "id": "dace77a6fa956375", 2226 | "type": "ui_spacer", 2227 | "z": "14ce0cc2506ae0e4", 2228 | "name": "spacer", 2229 | "group": "567c51cd17e47797", 2230 | "order": 66, 2231 | "width": 2, 2232 | "height": 1 2233 | }, 2234 | { 2235 | "id": "8ae745df2bc3fe29", 2236 | "type": "ui_spacer", 2237 | "z": "14ce0cc2506ae0e4", 2238 | "name": "spacer", 2239 | "group": "567c51cd17e47797", 2240 | "order": 67, 2241 | "width": 2, 2242 | "height": 1 2243 | }, 2244 | { 2245 | "id": "97c9801f1f554382", 2246 | "type": "ui_spacer", 2247 | "z": "14ce0cc2506ae0e4", 2248 | "name": "spacer", 2249 | "group": "567c51cd17e47797", 2250 | "order": 68, 2251 | "width": 2, 2252 | "height": 1 2253 | }, 2254 | { 2255 | "id": "e2ae1b943897c5cd", 2256 | "type": "ui_spacer", 2257 | "z": "14ce0cc2506ae0e4", 2258 | "name": "spacer", 2259 | "group": "567c51cd17e47797", 2260 | "order": 69, 2261 | "width": 2, 2262 | "height": 1 2263 | }, 2264 | { 2265 | "id": "584ca221a32912f8", 2266 | "type": "ui_spacer", 2267 | "z": "14ce0cc2506ae0e4", 2268 | "name": "spacer", 2269 | "group": "567c51cd17e47797", 2270 | "order": 70, 2271 | "width": 2, 2272 | "height": 1 2273 | }, 2274 | { 2275 | "id": "b66bb5e4a9ae4d84", 2276 | "type": "ui_spacer", 2277 | "z": "14ce0cc2506ae0e4", 2278 | "name": "spacer", 2279 | "group": "567c51cd17e47797", 2280 | "order": 71, 2281 | "width": 2, 2282 | "height": 1 2283 | }, 2284 | { 2285 | "id": "8ba00b97409ec340", 2286 | "type": "ui_spacer", 2287 | "z": "14ce0cc2506ae0e4", 2288 | "name": "spacer", 2289 | "group": "567c51cd17e47797", 2290 | "order": 72, 2291 | "width": 2, 2292 | "height": 1 2293 | }, 2294 | { 2295 | "id": "71fc235f097852e9", 2296 | "type": "ui_spacer", 2297 | "z": "14ce0cc2506ae0e4", 2298 | "name": "spacer", 2299 | "group": "567c51cd17e47797", 2300 | "order": 73, 2301 | "width": 2, 2302 | "height": 1 2303 | }, 2304 | { 2305 | "id": "0e0180d0b062f73e", 2306 | "type": "ui_spacer", 2307 | "z": "14ce0cc2506ae0e4", 2308 | "name": "spacer", 2309 | "group": "567c51cd17e47797", 2310 | "order": 75, 2311 | "width": 2, 2312 | "height": 1 2313 | }, 2314 | { 2315 | "id": "bf1b4488c88ab3b1", 2316 | "type": "ui_spacer", 2317 | "z": "14ce0cc2506ae0e4", 2318 | "name": "spacer", 2319 | "group": "567c51cd17e47797", 2320 | "order": 76, 2321 | "width": 2, 2322 | "height": 1 2323 | }, 2324 | { 2325 | "id": "05c38308449ea077", 2326 | "type": "ui_spacer", 2327 | "z": "14ce0cc2506ae0e4", 2328 | "name": "spacer", 2329 | "group": "567c51cd17e47797", 2330 | "order": 77, 2331 | "width": 2, 2332 | "height": 1 2333 | }, 2334 | { 2335 | "id": "6bdd61548ed6919e", 2336 | "type": "ui_spacer", 2337 | "z": "14ce0cc2506ae0e4", 2338 | "name": "spacer", 2339 | "group": "567c51cd17e47797", 2340 | "order": 78, 2341 | "width": 2, 2342 | "height": 1 2343 | }, 2344 | { 2345 | "id": "b16ceaee1a025a52", 2346 | "type": "ui_spacer", 2347 | "z": "14ce0cc2506ae0e4", 2348 | "name": "spacer", 2349 | "group": "567c51cd17e47797", 2350 | "order": 79, 2351 | "width": 2, 2352 | "height": 1 2353 | }, 2354 | { 2355 | "id": "6e5c5184c409c06b", 2356 | "type": "ui_spacer", 2357 | "z": "14ce0cc2506ae0e4", 2358 | "name": "spacer", 2359 | "group": "567c51cd17e47797", 2360 | "order": 80, 2361 | "width": 2, 2362 | "height": 1 2363 | }, 2364 | { 2365 | "id": "047d02ee46fe43c7", 2366 | "type": "ui_spacer", 2367 | "z": "14ce0cc2506ae0e4", 2368 | "name": "spacer", 2369 | "group": "567c51cd17e47797", 2370 | "order": 81, 2371 | "width": 2, 2372 | "height": 1 2373 | }, 2374 | { 2375 | "id": "84f48c81a018adaf", 2376 | "type": "ui_spacer", 2377 | "z": "14ce0cc2506ae0e4", 2378 | "name": "spacer", 2379 | "group": "567c51cd17e47797", 2380 | "order": 82, 2381 | "width": 2, 2382 | "height": 1 2383 | }, 2384 | { 2385 | "id": "ee2400fd3b867b8a", 2386 | "type": "ui_spacer", 2387 | "z": "14ce0cc2506ae0e4", 2388 | "name": "spacer", 2389 | "group": "567c51cd17e47797", 2390 | "order": 83, 2391 | "width": 2, 2392 | "height": 1 2393 | }, 2394 | { 2395 | "id": "592452ad925527a7", 2396 | "type": "mqtt-broker", 2397 | "name": "", 2398 | "broker": "192.168.49.104", 2399 | "port": "1883", 2400 | "clientid": "", 2401 | "autoConnect": true, 2402 | "usetls": false, 2403 | "protocolVersion": "4", 2404 | "keepalive": "60", 2405 | "cleansession": true, 2406 | "autoUnsubscribe": true, 2407 | "birthTopic": "", 2408 | "birthQos": "0", 2409 | "birthRetain": "false", 2410 | "birthPayload": "", 2411 | "birthMsg": {}, 2412 | "closeTopic": "", 2413 | "closeQos": "0", 2414 | "closeRetain": "false", 2415 | "closePayload": "", 2416 | "closeMsg": {}, 2417 | "willTopic": "", 2418 | "willQos": "0", 2419 | "willRetain": "false", 2420 | "willPayload": "", 2421 | "willMsg": {}, 2422 | "userProps": "", 2423 | "sessionExpiry": "" 2424 | }, 2425 | { 2426 | "id": "587f3511e9470eab", 2427 | "type": "ui_group", 2428 | "name": "Default", 2429 | "tab": "278e2f0e45278210", 2430 | "order": 1, 2431 | "disp": false, 2432 | "width": 20, 2433 | "collapse": false, 2434 | "className": "" 2435 | }, 2436 | { 2437 | "id": "d783cb980c3979fc", 2438 | "type": "ui_group", 2439 | "name": "Demo", 2440 | "tab": "0449b3b1fea99d52", 2441 | "order": 1, 2442 | "disp": true, 2443 | "width": "6", 2444 | "collapse": false 2445 | }, 2446 | { 2447 | "id": "0bbad19b45c1397b", 2448 | "type": "ui_group", 2449 | "name": "Group 1", 2450 | "tab": "6a73777eec002285", 2451 | "order": 1, 2452 | "disp": false, 2453 | "width": "6", 2454 | "collapse": false, 2455 | "className": "" 2456 | }, 2457 | { 2458 | "id": "48815cdd5546b75b", 2459 | "type": "server", 2460 | "name": "Home Assistant", 2461 | "version": 5, 2462 | "addon": false, 2463 | "rejectUnauthorizedCerts": true, 2464 | "ha_boolean": "y|yes|true|on|home|open", 2465 | "connectionDelay": true, 2466 | "cacheJson": true, 2467 | "heartbeat": false, 2468 | "heartbeatInterval": "30", 2469 | "areaSelector": "friendlyName", 2470 | "deviceSelector": "friendlyName", 2471 | "entitySelector": "friendlyName", 2472 | "statusSeparator": ": ", 2473 | "statusYear": "hidden", 2474 | "statusMonth": "short", 2475 | "statusDay": "numeric", 2476 | "statusHourCycle": "default", 2477 | "statusTimeFormat": "h:m", 2478 | "enableGlobalContextStore": false 2479 | }, 2480 | { 2481 | "id": "278e2f0e45278210", 2482 | "type": "ui_tab", 2483 | "name": "Mirror", 2484 | "icon": "dashboard", 2485 | "order": 2, 2486 | "disabled": false, 2487 | "hidden": false 2488 | }, 2489 | { 2490 | "id": "0449b3b1fea99d52", 2491 | "type": "ui_tab", 2492 | "name": "Demo", 2493 | "icon": "dashboard", 2494 | "disabled": false, 2495 | "hidden": false 2496 | }, 2497 | { 2498 | "id": "6a73777eec002285", 2499 | "type": "ui_tab", 2500 | "name": "Nether_Portal", 2501 | "icon": "fa-thermometer-half", 2502 | "order": 1, 2503 | "disabled": false, 2504 | "hidden": false 2505 | } 2506 | ] -------------------------------------------------------------------------------- /Software/node-red/test.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Projects/LocalJARVIS/fdb4f15f63d8ce969b9eae276161b53ea577f329/Software/node-red/test.glb --------------------------------------------------------------------------------