Node-RED를 사용한 시각적 프로그래밍: 쉽게 사물 인터넷 연결하기

게시 됨: 2022-03-11

프로그래밍을 통해 우리는 일련의 간단한 명령을 따라 기계가 복잡한 동작을 모방하도록 합니다. Assembly, C, Python 및 JavaScript와 같은 텍스트 프로그래밍 언어를 사용하는 것이 이를 수행하는 주요 방법 중 하나입니다. 이러한 프로그래밍 언어의 디자이너는 표현이 풍부한 구문, 강력한 프로그래밍 구성 및 강력한 도구 모음을 통해 가능한 한 쉽게 프로그램을 작성하는 경험을 만들기 위해 수많은 시간을 보냈습니다. 그러나 이러한 모든 프로그래밍 언어는 텍스트 소스 코드라는 공통된 특성을 공유합니다.

텍스트로 프로그램을 작성하는 것은 효과가 있으며 대부분의 경우 잘 작동합니다. 그러나 프로그램을 시각적으로 표현하는 능력은 종종 바람직합니다. 더 큰 시스템의 다양한 구성 요소를 통한 정보 흐름을 설계할 수 있는 것만으로도 충분합니다. 시각적 프로그래밍 도구는 프로그래밍이 처음이고 변수, 포인터, 신호, 범위 등과 같은 다양한 개념을 처리하는 데 어려움을 겪는 모든 사람에게 관대합니다.

Node-RED를 사용하여 API로 하드웨어 장치 연결

Node-RED는 시각적으로 프로그래밍하기 위한 도구입니다. 관계와 기능을 시각적으로 표시하고 사용자가 언어를 입력하지 않고도 프로그래밍할 수 있습니다. Node-RED는 노드를 추가하거나 제거하고 서로 통신할 수 있도록 함께 연결할 수 있는 브라우저 기반 흐름 편집기입니다.

Node-RED에서 모든 노드는 주입 노드 또는 기능 노드 의 두 가지 유형 중 하나입니다. 삽입 노드는 입력 없이 메시지를 생성하고 연결된 다음 노드로 메시지를 푸시합니다. 반면에 기능 노드는 입력을 받아 이에 대한 일부 작업을 수행합니다. 선택할 수 있는 노드가 너무 많기 때문에 Node-RED를 사용하면 하드웨어 장치, API 및 온라인 서비스를 그 어느 때보다 쉽게 ​​연결할 수 있습니다.

Node-RED 시작하기

Node-RED는 Node.js를 기반으로 합니다. Node-RED를 설치하려면 Node.js와 NPM이 모두 설치되어 있어야 합니다. NPM을 사용하면 Node-RED를 매우 쉽게 설치할 수 있습니다.

 npm install -g node-red

Node-RED의 흐름 편집기는 웹 브라우저 기반 응용 프로그램입니다. 이를 사용하려면 Node-RED를 실행하십시오.

 node-red

&hellip을 입력하고 http://localhost:1880으로 이동합니다.

안녕하세요, 월드입니다!

"Hello, world"라는 말을 배우지 않고도 어떤 초보자용 프로그래밍 튜토리얼이 완성될까요? 정확히 다음을 시도하여 시작하겠습니다.

  1. 흐름 편집기에서 주입 노드 를 끌어다 놓습니다. 그런 다음 두 번 클릭하고 페이로드를 문자열로 설정하고 "Hello world"를 작성합니다.
  2. 인젝션과 동일한 방식으로 디버그 노드 를 끌어다 놓습니다.
  3. 함께 배선하십시오.
  4. 오른쪽 모서리에 있는 "배포" 버튼을 클릭합니다.
  5. 주입 노드 바로 왼쪽에 있는 파란색 버튼을 클릭합니다.

시도 해봐. 다음과 같은 내용이 표시됩니다.

자바스크립트만

Node-RED를 사용하면 단순한 노드와 기능에 국한되지 않습니다. Node-RED는 Node.js를 기반으로 하기 때문에 모두 JavaScript로 구동됩니다. 노드는 실제로 Node.js 모듈입니다. http://flows.nodered.org/에서 찾을 수 있으므로 왼쪽 패널에 추가하려면 "npm install"만 하면 됩니다. 실제로 고유한 흐름을 개발하여 흐름 저장소에 업로드할 수 있습니다. Node-RED가 제공하는 코드 편집기 내의 함수 노드에 JavaScript를 입력할 수 있기 때문에 애플리케이션은 원하는 만큼 복잡할 수 있습니다.

플랫폼이 Node.js를 기반으로 하기 때문에 동일한 이벤트 기반 비차단 모델을 활용합니다. 따라서 Node-RED에 구축된 애플리케이션은 클라우드뿐만 아니라 Raspberry Pi와 같은 저가 하드웨어에서 실행할 수 있습니다.

이제 프로로 가자: 홈 오토메이션을 위한 시간

Node-RED가 사물 인터넷 영역에 어떻게 들어맞는지 보여주기 위해 스마트 전구의 색상을 변경하는 애플리케이션을 빌드해 보겠습니다. 모든 사람이 동일한 스마트 조명 시스템을 마음대로 사용할 수 있는 것은 아니지만 공식 흐름 저장소에서 적절한 Node-RED 모듈을 찾을 수 있으므로 걱정할 필요가 없습니다. 그러나 일을 더 쉽게 하기 위해 더 똑똑한 일을 하도록 합시다.

넷비스트를 만나보세요. 무선 프로토콜, 브랜드 호환성과 같은 세부 사항에 대해 걱정하거나 각 특정 API를 처리하는 방법을 알 필요 없이 사물 인터넷 기기 및 장치용 애플리케이션을 개발하기 위한 오픈 소스 플랫폼입니다. 실제 장치처럼 작동하는 가상 장치를 사용할 수 있습니다! 따라서 스마트 전구가 없더라도 가상 전구를 사용할 수 있습니다.

다음과 같이 전역적으로 Node-RED npm 패키지용 Netbeast를 설치할 수 있습니다.

 npm install -g node-red-contrib-netbeast

netbeast-red 노드 는 Netbeast 대시보드를 나타내며, 이 대시보드는 API 기본 요소를 집에 있는 모든 스마트 장치로 변환합니다. 다행히 모듈로도 사용할 수 있습니다!

Netbeast 시작:

 npm install -g netbeast-cli netbeast start

이렇게 하면 포트 8000에서 대시보드를 사용할 수 있고 8443에서 SSL을 사용할 수 있습니다. 그런 다음 브라우저에서 http://localhost:8000을 열고 탐색으로 이동합니다. 거기에서 많은 앱과 플러그인을 찾을 수 있습니다. 스마트 전구 브랜드(Philips Hue, LIFX, WeMo)를 찾아보거나 없는 경우 전구 플러그인을 다운로드해 보십시오. 대시보드 플러그인에 다음 중 하나가 포함되어 있는지 확인하십시오!

노란색 배지는 플러그인이 실행 중이지만 장치를 찾을 수 없음을 나타냅니다. 전구 플러그인을 클릭하여 가상 전구를 만듭니다. 검색된 다른 모든 장치는 네트워크 아래에 나타나야 합니다.

모든 것이 제자리에 있으면 작업으로 돌아갑시다. 우리는 간단한 흐름을 만들 것입니다:

  1. 주입 노드를 끌어다 놓습니다.
  2. Netbeast 노드를 끌어다 놓습니다.
  3. 디버그 노드를 끌어다 놓습니다.
  4. 아래와 같이 모두 연결합니다.

이제 대시보드에 HTTP 요청을 보내보겠습니다. Netbeast API를 사용하여 전구에서 트리거하려는 값이 포함된 JSON을 삽입 노드를 통해 보내야 합니다.

버튼을 눌러 모든 스마트 전구에 색상과 전원을 주입하세요!

각 주제는 다른 종류의 장치를 나타냅니다. 조명뿐만 아니라 음악, 난방 및 비디오에 대한 주제도 있습니다. 습도, 존재, 온도 및 목록에 대한 센서뿐만 아니라 계속됩니다. 문서에서 주제 목록과 모든 종류의 장치로 번역할 권장 구조를 찾을 수 있습니다. 이 IoT 엔진은 미숙하지만 강력합니다. 개발자가 정보를 재사용하여 진정으로 연결된 시나리오를 생성할 수 있도록 하는 오픈 소스입니다.

더 깊이 가자

다음으로, 주변 소음 감지기라는 또 다른 플러그인을 사용하여 두 번째 흐름을 만들어 전구의 색상을 소음 세마포어로 변경하는 트리거로 사용합니다. 이 자습서에서는 가상 하드웨어를 사용하므로 새 하드웨어를 구입할 필요가 없습니다. Node-RED 편집기에서 "더하기" 버튼을 클릭하여 시작하겠습니다.

대시보드 http://localhost:8000/explore에서 탐색 섹션으로 다시 이동하여 Volume-Plugin을 찾습니다. 브라우저 내에서 getUserMedia() 를 활용하여 간단한 HTML 앱에서 미디어를 캡처하는 매우 기본적인 웹 앱입니다. 따라서 아마도 최신 브라우저에서만 작동할 것입니다!

가상 전구와 마찬가지로 열려면 클릭하십시오. 마이크에서 녹음할 수 있는 권한을 요청합니다. 그런 다음 Netbeast의 MQTT 브로커로 메시지를 보내며 이 브로커는 모든 대시보드에서 공유되므로 구독할 수 있습니다. 이러한 작업을 수행하려면 netbeast-trigger 노드를 node-red의 편집기로 끌어다 놓기만 하면 됩니다. 그런 다음 트리거와 Netbeast 노드 사이에 기능을 삽입하여 소리가 너무 큰지 아닌지를 결정합니다. 또한 일부 디버그 노드를 사용하여 모든 것이 괜찮은지 확인해야 합니다. 이제 계획은 다음과 같이 보일 것입니다.

이제 tooLoud 함수 노드에 일부 코드를 입력해 보겠습니다. 예, 코딩하지 않고도 프로그래밍할 수 있다고 약속했지만 이미 할 수 있음을 보여주었습니다! 그리고 사용 가능한 다른 요소나 레지스트리의 다른 노드를 결합하여 다음을 수행할 수 있습니다.

 var volume = msg.payload.volume node.log(volume) if (volume < 50) { return { topic: 'lights', payload: { power: 1, color: '#00CC00'}} } else if (volume < 75) { return { topic: 'lights', payload: { power: 1, color: '#CCCC00'}} } else { return { topic: 'lights', payload: { power: 1, color: '#FF0000'}} }

이 다소 간단한 코드 조각은 이전 노드에서 보고한 볼륨 수준에 따라 특정 색상 코드로 다음 노드에 대한 세 가지 페이로드 중 하나를 반환합니다.

이제 갈 준비가 되었습니다! Deploy 버튼을 다시 누르고 약간의 소음을 냅시다. 전구가 한 색상에서 다른 색상으로 즉시 어떻게 변하는지 봅시다!

사용 중인 마이크와 웹 브라우저가 다를 수 있으므로 기능 값과 임계값을 자유롭게 조정하고 색상 값을 사용하여 전구가 어떻게 변하는지 확인하십시오.

나만의 플러그인 만들기

순수 CSS의 이 전구는 이 cssdeck에서 영감을 받았습니다.

눈치채셨겠지만, 이전의 가상 전구는 매우 기초적이므로 조정하는 것이 좋습니다. 또는 더 나은 방법으로 자신만의 스마트 홈 컨트롤러를 만들 수 있습니다. 따라서 Netbeast용 가상 플러그인을 만드는 과정을 살펴보겠습니다. 이를 통해 스마트 장치용 컨트롤러를 직접 만들 수 있습니다.

netbeast-cli 패키지를 사용하여 일부 코드를 자동으로 생성할 수 있습니다. netbeast create myplugin --plugin 을 실행하면 다음과 같은 기본 프로젝트가 완성됩니다.

 myplugin ├── README.md ├── index.js ├── package.json └── test.js

프론트엔드

이제 프론트엔드로 전구를 모방해 봅시다. 장치 컨트롤러에는 일반적으로 하나가 없으므로 scaffold 명령에 아직 공용 폴더가 포함되어 있지 않습니다. 프로젝트 내부에 public 디렉토리를 만들고 여기에 다음 HTML, CSS 및 JS 파일을 배치해 보겠습니다.

index.html

 <head> <title>Netbeast Bulb Plugin</title> <link rel="stylesheet" href="bulb.css" media="screen" charset="utf-8"> </head> <body> <div class="top-decoration"></div> <div> </div> <div class="bulb-container small"> <div class="bulb light"> <div> <div class="bulb top"></div> <div class="bulb middle-1"></div> <div class="bulb middle-2"></div> <div class="bulb middle-3"></div> <div class="bulb bottom"></div> </div> <div> <div class="screw-top"></div> <div class="screw a"></div> <div class="screw b"></div> <div class="screw a"></div> <div class="screw b"></div> <div class="screw a"></div> <div class="screw b"></div> <div class="screw c"></div> <div class="screw d"></div> </div> </div> </div> <div class="code-container"> <pre><i class="txt-red">beast</i>(<i class="txt-green">'lights'</i>).<i class="txt-blue">set</i>({ <i class="txt-green">color</i>: <i class="txt-green">"<input type="text" class="color" name="color" value="00fea5">"</i>, <i class="txt-green">power</i>: <i class="txt-green">"<input type="text" class="power" name="power" value="on">"</i> })</pre> <button> RUN </button> </div><!-- wrapper --> <!-- declares bulb features and methods --> <script type="text/javascript" src="bulb.js"></script> <!-- real time comms library --> <script type="text/javascript" src="socketio.js"></script> <!-- simulates hardware communication --> <script type="text/javascript" src="hw-api.js"></script> </body>

전구.css

 section { float: left; padding: 20px 50px 20px 50px; } .bulb-light { border: 0; background: transparent; margin: 0 auto !important; padding: 0 !important; display: block; z-index: 1; } #bulb { opacity: 1; z-index: 3; display: block;} .bulb.top { border: 0; width: 300px; height: 300px; margin: 0 auto; padding: 0; border-radius: 999px; background: #E7E7E7; } .bulb.middle-1 { margin: -75px auto 0 auto; width: 190px; border-left: 35px solid transparent; border-right: 35px solid transparent; border-top: 55px solid #E7E7E7; } .bulb.middle-2 { margin: -22px auto 0 auto; width: 178px; border-left: 19px solid transparent; border-right: 19px solid transparent; border-top: 50px solid #E7E7E7; } .bulb.middle-3 { margin: -20px auto 0 auto; width: 182px; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 30px solid #E7E7E7; } .bulb.bottom { width: 184px; height: 65px; margin: -8px auto 0 auto; padding: 0; border-radius: 0 0 999px 999px; background: #E7E7E7; } #base { position:relative; z-index: 2; } .screw { transform: rotate(-3deg); -ms-transform: rotate(-3deg); -webkit-transform: rotate(-3deg); padding: 0; } .screw-top { margin: -18px auto -4px auto; padding: 0; width: 132px; height: 0; border-left: 15px solid transparent; border-right: 15px solid transparent; border-top: 21px solid #D3D3D3; border-radius: 999px; } .screw.a { background: #DDD; width: 150px; height: 15px; border-radius: 999px; margin: -1px auto 0px; } .screw.b { background: #D9D9D9; width: 135px; height: 15px; margin: -1px auto 0px; } .screw.c { margin: -1px auto 0px; width: 78px; height: 0; border-left: 30px solid transparent; border-right: 30px solid transparent; border-top: 20px solid #DDD; border-radius: 8px; } .screw.d { margin: 0 auto; width: 15px; height: 0; border-left: 30px solid transparent; border-right: 30px solid transparent; border-top: 15px solid #444; } .on #light { -moz-opacity: 1; -khtml-opacity: 1; opacity: 1; } .bulb.top, .bulb.bottom { transition: all 0.5s ease-in-out; } .bulb.middle-1, .bulb.middle-2, .bulb.middle-3 { transition: all 0.5s ease-in-out; }

이러한 HTML 및 CSS 파일을 사용하면 브라우저에서 이미 전구 모양을 볼 수 있어야 합니다. HTML 파일을 열어 라이브로 확인하세요! 작동 되나요? 좋습니다. 이제 일부 기능을 제공하겠습니다.

전구.js

이 파일은 간단한 클릭으로 전구 동작을 모방하고 Netbeast를 통해 색상을 변경하는 데 잠시 사용되는 몇 가지 기능을 설정합니다.

 var color = document.getElementById('color') var power = document.getElementById('power') var bulb = document.getElementById('bulb') var button = document.getElementById('run-btn') var light = document.getElementById('light') button.onclick = function toggleBulbState () { changeBulbParams({ color: color.value, power: power.value }) } function setBulbParams (params) { if (params.power === 'off') { params = { color: 'E7E7E7' } } console.log('set params', params) var bulb_parts = ['.bulb.middle-1', '.bulb.middle-2', '.bulb.middle-3'] document.querySelector('.bulb.top').style.boxShadow = '0px 0px 98px #' + params.color document.querySelector('.bulb.top').style.backgroundColor = params.color document.querySelector('.bulb.bottom').style.backgroundColor = params.color bulb_parts.forEach(function (className) { document.querySelector(className).style.borderTopColor = params.color }) } function changeBulbParams (params) { console.log('change params', params) /* Overwrite html fields if necessary */ color.value = params.color || color.value power.value = params.power || power.value setBulbParams({color: color.value, power: power.value}) }

그런 다음 필드와 실행 버튼이 모두 의미가 있어야 합니다. 새로운 가상 전구에서 다양한 색상을 시험해 볼 수 있습니다. 그러나 우리가 여기까지 온 이유는 사물 인터넷 생태계의 또 다른 장치로 만들기 위해서였습니다.

hw-api.js

자체 제작한 프론트엔드 JS의 마지막입니다. WiFi 또는 Bluetooth 전구가 전화, 서버 또는 허브와 같은 원격 컨트롤러로 수행하는 것처럼 서버와의 무선 연결을 조롱합니다. 이것은 실제 플러그인 코드가 그것을 제어하는 ​​데 사용할 인터페이스입니다!

 var socket = io.connect() socket.on('connect', function () { console.log('ws:// bulb is online') }) socket.on('disconnect', function () { console.log('ws:// connection with bulb lost') }) socket.on('set', function (params) { changeBulbParams(params) // uses functions from bulb.js! }) socket.on('get', function () { const params = { power: power.value, color: color.value } socket.emit('params', params) })

마지막으로 HTML에서 포함할 WebSocket 라이브러리가 필요하므로 프론트엔드가 준비됩니다. https://raw.githubusercontent.com/netbeast/bulb-plugin/master/public/socketio.js에서 소스를 복사하여 socketio.js 라는 파일에 붙여넣을 수 있습니다. curl 또는 wget 이 있는 터미널에서 다음과 같이 간단하게 수행할 수 있습니다.

 curl https://raw.githubusercontent.com/netbeast/bulb-plugin/master/public/socketio.js > public/socketio.js

지금쯤이면 다음과 같은 파일 구조를 갖게 될 것입니다.

 myplugin ├── README.md ├── index.js ├── package.json ├── public │ ├── bulb.css │ ├── bulb.js │ ├── hw-api.js │ ├── index.html │ └── socketio.js └── test.js

백엔드

이제 장치와 인터페이스를 구현하고 Netbeast 엔진에 등록할 것입니다. 전구가 네트워크에 설치되었음을 감지하기 위해 웹 소켓을 수신 대기한 다음 새 리소스를 사용할 수 있도록 대시보드 API에 POST 를 수행합니다.

이를 위해 이전에 생성한 파일을 살펴보겠습니다.

패키지.json

이 파일에는 앱을 실행하는 데 필요한 모든 종속성과 정보가 포함되어 있습니다. Netbeast는 또한 일반 package.json 을 사용하여 이름 또는 유형과 같은 일부 정보를 검색합니다. 이 패키지가 플러그인임을 지정하는 것이 중요합니다!

 { "name": "myplugin", "version": "0.0.0", "description": "Netbeast plugin for... <your description>", "main": "index.js", "netbeast": { "bootOnLoad": true, "type": "plugin" }, "dependencies": { "bluebird": "^3.3.5", "body-parser": "^1.15.0", "express": "^4.13.4", "minimist": "^1.2.0", "mocha": "^2.3.2", "morgan": "^1.6.1", "netbeast": "^1.0.6", "socket.io": "^1.4.5", "superagent": "^1.8.3" }, "devDependencies": {}, "scripts": { "test": "node test.js", "start": "node index.js" }, "repository": { "type": "git", "url": "GITHUB_REPOSITORY" }, "keywords": [ "iot", "netbeast", "plugin" ], "author": "YOUR_EMAIL", "license": "GPL 3", "bugs": { "url": "ISSUES_CHANNEL" }, "homepage": "HOMEPAGE" }

index.js

플러그인을 실행하기 위해 Netbeast 대시보드에서 호출되는 코드입니다! 들어오는 요청을 수락할 위치를 알기 위해 명령줄 인수로 포트를 수락해야 합니다. node myplugin.js --port <a free port number> 를 입력한 것처럼 실행됩니다. 초반에 해시뱅도 참고해주세요! #!/usr/bin/env node .

 #!/usr/bin/env node var io = require('socket.io')() var express = require('express') var bodyParser = require('body-parser') var app = express() // Netbeast apps need to accept the port to be launched by parameters var argv = require('minimist')(process.argv.slice(2)) app.use(express.static('public')) // will serve our app in an HTTP server app.use(bodyParser.json()) // will parse JSON API calls app.use('/api', require('./plugin')(io)) var server = app.listen(argv.port || 31416, function () { console.log('Bulb plugin listening at http://%s:%s', server.address().address, server.address().port) }) // we need websockets to push updates to browser view io.listen(server)

보시다시피 실제로 socket.io 컨트롤러를 구현하는 파일을 실행하기 위한 파일이 누락되었습니다. 멋진 건 없어!

플러그인.js

 var express = require('express') var netbeast = require('netbeast') var router = express.Router() var bulbParams // auxiliar variable, nasty way to transmit changes, but works module.exports = function (io) { io = io // Create resource that works on lights topic and listens on /api route netbeast('lights').create({ app: 'myplugin', hook: '/api' }) io.on('connection', function () { console.log('ws:// bulb has connected to plugin') }) io.on('disconnection', function () { console.log('ws:// bulb has disconnected from plugin') }) io.on('connect_failure', function (err) { console.trace(err) }) router.post('/', function (req, res) { io.emit('set', { power: req.body.power, color: req.body.color, }) res.status(200).json(req.body) }) router.get('/', function (req, res) { io.emit('get') var timerReference = setTimeout(function () { if (bulbParams) { res.json(bulbParams) } else { res.status(200).json({ error: 'No bulb available' }) } }, 3000) }) return router }

앱 실행

이제 앱을 테스트할 때입니다. tar.gz 형식으로 패키징하고 드래그 앤 드롭 섹션 http://localhost:8000/install의 대시보드에 업로드하면 됩니다.

 beast package # Compresses your app when ran in myplugin dir

짜잔! 이제 플러그인으로 이동하여 테스트할 수 있습니다. 네트워크 섹션(http://localhost:8000/devices)으로 이동하여 실행 중인지 확인하고 거기에서 색상을 변경합니다.

문제가 발생하거나 세부 정보를 놓쳤다고 생각되면 node index.js 를 사용하여 로컬에서 실행해 보십시오. netbeast start 로그 내에서보다 디버그하기가 더 쉬울 것입니다.

작업 게시

앱이 Netbeast 대시보드 탐색 섹션에 표시되도록 하려면 설명 및 README.md 에 모두 포함된 Netbeast 앱 또는 Netbeast 플러그인을 사용하여 GitHub에 리포지토리를 생성해야 합니다.

앱을 찾기 위해 GitHub의 검색 API를 사용합니다. https://api.github.com/search/repositories?q=netbeast+language:javascript에 GET 요청을 할 때 나타나는 것과 동일한 결과를 봅니다.

앱이 거기에 나타나면 앱이 표시된다는 것을 알게 될 것입니다!

무엇 향후 계획?

두 프로젝트 모두 오픈 소스이며 실제로 커뮤니티에 참여했습니다. Node-RED에 대한 고유한 흐름 또는 노드 생성을 시작하려면 공식 문서를 살펴보십시오. 거기에 설명된 단계를 따르면 자신의 노드 또는 흐름을 즉시 게시할 수 있습니다.

반면에 Netbeast 내부를 살펴보고 싶다면 해당 설명서를 따르거나 대시보드 리포지토리를 살펴볼 수 있습니다. Netbeast API를 사용하면 더 이상 개별 장치, 브랜드 또는 기술에 집중할 필요가 없으므로 시도해 보십시오. 여기에서 자세히 알아보거나 Slack 채널에 가입하여 Node-RED, IoT 또는 Node.js에 대해 토론할 수 있습니다.

Raspberry Pi, Beagle Bone 또는 이전 서버에 이 항목을 설치하려는 경우 코드 없이 해킹 가능한 Smart Hub로 전환할 수 있습니다! 두 사이트 모두에 미리 만들어진 빌드가 있습니다.

즐거운 해킹.

관련: 우리는 안전하지 않은 사물 인터넷(IoT)을 만들고 있습니까?