├── .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 | 
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

\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
--------------------------------------------------------------------------------