├── .gitignore
├── .gitmodules
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE
├── MAINTAINERS.md
├── README-ko.md
├── README.md
├── audio
├── .asoundrc
├── .asoundrc.matrix
├── README-ko.md
├── README.md
├── install_matrix_deps.sh
└── requirements.txt
├── ibm_cloud_functions
├── clock.js
├── conversation.js
├── iot-pub.py
├── nlc.js
└── requirements.txt
├── ibm_cloud_services
├── conversation
│ └── conversation.json
├── node_red
│ └── node-red.json
└── twilio
│ ├── .env.example
│ ├── README.md
│ ├── app.js
│ └── package.json
├── images
├── Architecture.jpg
├── Architecturev2.jpg
├── adddevice.png
├── adddevice_menu.png
├── configure_messaging.png
├── configure_messaging_generic.png
├── createdevicetype.png
├── devicecreds.png
├── generateapikey_menu.png
├── gpio_output.png
├── hardware_arch.jpeg
├── home_automation.png
├── home_automation_bb.jpg
├── home_automation_bb.png
├── home_automation_bb.svg
├── home_automation_labeled.png
├── home_automation_screen.png
├── iotwelcome.png
├── noderedscreen.png
├── serverless_flow.png
├── service_create.png
├── service_find.png
├── shortened_fritzing.jpg
├── stt_creds.png
├── twilio_phone_numbers.png
└── watsoniotimage.png
└── iot-gateway
├── README.md
├── devices.json
├── install_deps.sh
├── node-mqtt.js
└── node-mqtt.service
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Dependency directories
7 | node_modules/
8 |
9 | # environment variables file
10 | .env
11 |
12 | .DS_Store
13 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "audio/snowboy"]
2 | path = audio/snowboy
3 | url = https://github.com/kkbankol-ibm/snowboy.git
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 | - "3.6"
5 | install:
6 | - "pip install flake8"
7 | script:
8 | - flake8 . --ignore E501,F841,E302
9 | git:
10 | submodules: false
11 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | This is an open source project, and we appreciate your help!
4 |
5 | We use the GitHub issue tracker to discuss new features and non-trivial bugs.
6 |
7 | In addition to the issue tracker, [#journeys on
8 | Slack](https://dwopen.slack.com) is the best way to get into contact with the
9 | project's maintainers.
10 |
11 | To contribute code, documentation, or tests, please submit a pull request to
12 | the GitHub repository. Generally, we expect two maintainers to review your pull
13 | request before it is approved for merging. For more details, see the
14 | [MAINTAINERS](MAINTAINERS.md) page.
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/MAINTAINERS.md:
--------------------------------------------------------------------------------
1 | # Maintainers Guide
2 |
3 | This guide is intended for maintainers - anybody with commit access to one or
4 | more Code Pattern repositories.
5 |
6 | ## Methodology
7 |
8 | This repository does not have a traditional release management cycle, but
9 | should instead be maintained as a useful, working, and polished reference at
10 | all times. While all work can therefore be focused on the master branch, the
11 | quality of this branch should never be compromised.
12 |
13 | The remainder of this document details how to merge pull requests to the
14 | repositories.
15 |
16 | ## Merge approval
17 |
18 | The project maintainers use LGTM (Looks Good To Me) in comments on the pull
19 | request to indicate acceptance prior to merging. A change requires LGTMs from
20 | two project maintainers. If the code is written by a maintainer, the change
21 | only requires one additional LGTM.
22 |
23 | ## Reviewing Pull Requests
24 |
25 | We recommend reviewing pull requests directly within GitHub. This allows a
26 | public commentary on changes, providing transparency for all users. When
27 | providing feedback be civil, courteous, and kind. Disagreement is fine, so long
28 | as the discourse is carried out politely. If we see a record of uncivil or
29 | abusive comments, we will revoke your commit privileges and invite you to leave
30 | the project.
31 |
32 | During your review, consider the following points:
33 |
34 | ### Does the change have positive impact?
35 |
36 | Some proposed changes may not represent a positive impact to the project. Ask
37 | whether or not the change will make understanding the code easier, or if it
38 | could simply be a personal preference on the part of the author (see
39 | [bikeshedding](https://en.wiktionary.org/wiki/bikeshedding)).
40 |
41 | Pull requests that do not have a clear positive impact should be closed without
42 | merging.
43 |
44 | ### Do the changes make sense?
45 |
46 | If you do not understand what the changes are or what they accomplish, ask the
47 | author for clarification. Ask the author to add comments and/or clarify test
48 | case names to make the intentions clear.
49 |
50 | At times, such clarification will reveal that the author may not be using the
51 | code correctly, or is unaware of features that accommodate their needs. If you
52 | feel this is the case, work up a code sample that would address the pull
53 | request for them, and feel free to close the pull request once they confirm.
54 |
55 | ### Does the change introduce a new feature?
56 |
57 | For any given pull request, ask yourself "is this a new feature?" If so, does
58 | the pull request (or associated issue) contain narrative indicating the need
59 | for the feature? If not, ask them to provide that information.
60 |
61 | Are new unit tests in place that test all new behaviors introduced? If not, do
62 | not merge the feature until they are! Is documentation in place for the new
63 | feature? (See the documentation guidelines). If not do not merge the feature
64 | until it is! Is the feature necessary for general use cases? Try and keep the
65 | scope of any given component narrow. If a proposed feature does not fit that
66 | scope, recommend to the user that they maintain the feature on their own, and
67 | close the request. You may also recommend that they see if the feature gains
68 | traction among other users, and suggest they re-submit when they can show such
69 | support.
70 |
--------------------------------------------------------------------------------
/README-ko.md:
--------------------------------------------------------------------------------
1 | ## 자연어 인터페이스 기반의 홈오토메이션
2 |
3 | *다른 언어로 보기: [English](README.md).*
4 |
5 | 지난 몇 년 동안 Apple Siri, Amazon Alexa 및 Google Assistant 등의 지능형 개인 비서의 인기가 크게 증가했습니다. 이러한 앱들은 초창기에는 신기한 장난감 정도로 인식되었지만, 이제는 서비스 API 및 IoT 연결 디바이스와 상호작용하는 편리한 인터페이스로 발전했습니다. 이 개발 과정은 Raspberry Pi를 사용하여 전원 콘센트를 껐다가 켜서 사용자가 자체 홈 허브를 설정하는 방법을 배우게됩니다. 회로 및 소프트웨어 의존성이 올바르게 설치되고 구성되면 IBM Watson의 언어 서비스를 사용하여 음성 또는 텍스트 명령으로 전원 콘센트를 제어할 수 있게 됩니다. 또한 OpenWisk 서버리스 기능을 사용하여 예정된 스케줄, 기상 변화, 동작 센서 활성화 등을 기반으로 이러한 소켓을 트리거하는 방법을 보여줍니다.
6 |
7 | ### 아키텍처
8 | 
9 |
10 | *아키텍처 구성도*
11 | 1. 사용자는 마이크에 대고 명령을 말하거나 Twilio SMS 번호로 텍스트를 보냅니다
12 | 2. 해당 명령이 캡처되어 IBM Cloud Functions 시퀀스를 트리거하는 HTTP POST 요청에 임베드됩니다
13 | 3. IBM Cloud Functions 액션 1은 오디오를 Bluemix Speech to Text 서비스로 전달하고 응답을 기다립니다
14 | 4. 텍스트화된 명령은 IBM Cloud Functions 액션 2로 전달됩니다
15 | 5. IBM Cloud Functions 액션 2는 Watson Assistant 서비스를 호출하여 사용자의 텍스트 명령을 분석한 다음 응답을 기다립니다
16 | 6. Watson Assistant 서비스 결과는 최종 IBM Cloud Functions 액션으로 전달됩니다
17 | 7. IBM Cloud Functions 액션은 IoT MQTT 브로커에 엔티티 / 인텐트 페어(예 : "fan / turnon")를 게시합니다
18 | 8. MQTT 브로커에 등록된 Raspberry Pi는 결과를 수신합니다
19 | 9. Raspberry Pi는 RF 신호를 전송하여 콘센트를 켜거나 끕니다
20 |
21 |
22 | ### 설정 단계
23 | - [하드웨어 연결 및 구성](#하드웨어-부품-설정하기)
24 | * RF 회로 조립
25 | * 소프트웨어 종속성 + 라이브러리 설치
26 | * 무선 소켓에 응당하는 RF 코드 캡처
27 | - [Bluemix 서비스 제공하기](#플랫폼-서비스-제공-및-설정하기)
28 | - [서버리스 기능 생성하기](#IBM Cloud Functions)
29 | - [Bluemix에 배포하기](#bluemix)
30 |
31 | ## 하드웨어 부품 설정하기
32 |
33 | RF 회로를 조립하고 구성부터 시작하겠습니다. 이 회로에는 다음 구성 요소가 필요합니다
34 | - [Raspberry Pi 3](https://www.raspberrypi.org/products/raspberry-pi-3-model-b/)
35 | - [GPIO 리본 케이블 + 브레이크아웃 보드](https://www.adafruit.com/product/914)
36 | - [433MHz RF 송신기 및 수신기](https://www.amazon.com/SMAKN%C2%AE-433Mhz-Transmitter-Receiver-Arduino/dp/B00M2CUALS)
37 | - [Etekcity 433 MHz 아웃렛](https://www.amazon.com/Etekcity%C2%AE-Wireless-Control-Switches-included/dp/B00DQELHAE/)
38 | - [브레드보드](https://www.adafruit.com/product/239)
39 | - [USB 마이크](https://www.amazon.com/eBerry-Adjustable-Microphone-Compatible-Recording/dp/B00UZY2YQE/ref=sr_1_9?ie=UTF8&qid=1497828013&sr=8-9&keywords=usb+microphone)
40 |
41 | 모든 부품을 준비하신 후 조립하여 아래 회로를 구성하십시오. 이 회로에는 GPIO 리본 / 브레이크아웃 보드를 통해 브레드보드에 연결된 Raspberry Pi가 포함됩니다.
42 |
43 | 
44 |
45 |
46 |
47 |
50 |
51 | 브레이크아웃 보드의 왼쪽에 있는 빨간색 전선은 Raspberry Pi에서 브레드보드의 전원 레일 중 하나로의 5V 브리징을 합니다. 다이어그램의 오른쪽 하단에 있는 추가 빨간색 전선은 파워 레일에서 RF 수신기 및 송신기로 5V를 공급합니다. 흰색 전선도 유사한 개념이지만, 이 와이어는 일반적으로 "접지"로 불리는 음전하를 제공합니다. 다음으로, 초록색 전선은 Raspberry Pi의 GPIO 핀 17을 트랜스미터의 데이터 핀에 연결하고 검은색 전선은 GPIO 핀 27을 수신기의 데이터 핀에 연결합니다. 그 이유는 아래 이미지 `gpio readall` 출력에서 볼 수 있듯이, BCM 17에 매핑되는 송신기가 [wiringPi pin 0](https://github.com/ninjablocks/433Utils/blob/master/RPi_utils/codesend.cpp#L27)을 기본값으로하기 때문에 BCM 27에 매핑되는 수신기는 [wiringPi pin 2](https://github.com/ninjablocks/433Utils/blob/master/RPi_utils/RFSniffer.cpp#L25)로 기본 설정됩니다. 이러한 기본 핀은 433Utils 라이브러리의 링크된 파일 중 하나를 수정하고 라이브러리를 다시 컴파일하여 변경할 수 있습니다.
52 |
53 | 일단 Raspberry Pi가 회로에 연결되면 RF 송신기 및 수신기와 상호 작용할 수 있도록 종속적인 소프트웨어를 설치해야합니다. 이는 [install_deps.sh] (./iot- gateway / install_deps.sh) 스크립트를 실행하여 수행할 수 있습니다.
54 |
55 | 여기에 설치되는 오픈소스 라이브러리는 [wiringPi] (http://wiringpi.com/) 및 [433Utils] (https://github.com/ninjablocks/433Utils)입니다. wiringPi를 사용하면 응용 프로그램에서 Raspberry Pi의 GPIO 핀을 읽고 제어할 수 있습니다. 433Utils는 433MHz 주파수로 메시지를 송수신하기 위해 wiringPi 라이브러리를 호출합니다. 이 예제의 경우, 각 콘센트에는 전원을 켜고 끌 수 있는 고유한 RF 코드가 있습니다. 각각의 고유 코드를 필수적으로 등록하기 위해 "RFSniffer"라는 wiringPi 유틸리티 중 하나를 사용합니다. 433MHz 주파수는 온도 조절기, 창문/문 센서, 자동차 키 등과 같은 많은 일반 디바이스에서 표준으로 쓰입니다. 따라서 이 초기 설정은 전원 콘센트 제어에만 국한되는 것은 아닙니다.
56 |
57 | 스크립트가 완료되면 `gpio readall`을 실행하여 wiringPi가 성공적으로 설치되었는지 확인하십시오. 다음 차트가 표시되어야합니다.
58 |
59 |
60 |
61 |
62 | 이제 어떤 RF 코드가 Etekcity 전원에 해당하는지 확인할 수 있습니다. 아래 코드를 실행해서 시작하세요.
63 | ```
64 | sudo /var/www/rfoutlet/RFSniffer
65 | ```
66 |
67 | 그러면 RF 수신기에서 들어오는 신호를 수신하여 표준 출력에 씁니다. Etekcity 리모컨의 켜기/끄기 버튼을 누르면 회로가 올바르게 배선된 경우 Raspberry Pi에 다음이 나타납니다.
68 | ```
69 | pi@raspberrypi:~ $ sudo /var/www/rfoutlet/RFSniffer
70 | Received 5528835
71 | Received pulse 190
72 | Received 5528844
73 | Received pulse 191
74 | ```
75 |
76 | RF 소켓의 on/off 신호를 결정한 후, 캡처된 신호를 / etc / environment 파일에 배치하십시오.
77 | ```
78 | RF_PLUG_ON_1=5528835
79 | RF_PLUG_ON_PULSE_1=190
80 | RF_PLUG_OFF_1=5528844
81 | RF_PLUG_OFF_PULSE_1=191
82 | ```
83 |
84 | 이제 연결된 소켓을 연결하고 다음 명령을 실행하여 Raspberry Pi가 소켓을 켜고 끌 수 있는지 확인하십시오. 이 명령은 요청된 펄스 길이로 RF 코드를 보내며, 이는 -l 파라미터로 제공됩니다.
85 |
86 | ```
87 | source /etc/environment
88 | /var/www/rfoutlet/codesend ${RF_PLUG_ON_1} -l ${RF_PLUG_ON_PULSE_1}
89 | /var/www/rfoutlet/codesend ${RF_PLUG_OFF_1} -l ${RF_PLUG_OFF_PULSE_1}
90 | ```
91 |
92 | 이제는 cli를 통해 소켓을 수동으로 제어할 수 있기 때문에, 이 과정에서는 여러 가지 자동화된 방법으로 제어하는 것을 시도해 보겠습니다. Raspberry Pi에서 파이프라인과 복잡한 자동화 로직을 작성하고 실행하는 대신 IBM Cloud Functions 서버리스 이벤트-기반 플랫폼을 활용할 것입니다. 이 구현에서 IBM Cloud Functions 액션은 MQTT 메시지를 통해 Raspberry Pi와 통신합니다.
93 |
94 | ### 오디오 인터페이스
95 | 일단 Raspberry Pi가 설정되면 USB 마이크의 오디오 입력을 인식하도록 설정해야 합니다. 필요한 경우에만 오디오를 녹음하고 텍스트화하기 위해 [Snowboy] (https://snowboy.kitt.ai/)라는 "Hotword"검색 서비스를 활용하여 특정 음성 패턴 (이 예제의 경우,** Hello Watson**)을 선택하고 핫 워드 패턴이 감지되면 녹음을 시작합니다. 음성 모델을 만드는 데 필요한 단계는 [여기](http://docs.kitt.ai/snowboy/)에서 찾을 수 있습니다.
96 |
97 |
98 | ## 플랫폼 서비스 제공 및 설정하기
99 | - [Watson Assistant](https://console.bluemix.net/catalog/services/conversation)
100 | - [Speech to Text](https://console.bluemix.net/catalog/services/speech-to-text)
101 | - [Watson IoT Platform](https://console.bluemix.net/catalog/services/internet-of-things-platform)
102 | - [Twilio](https://console.bluemix.net/catalog/services/twilio)
103 |
104 |
105 | 이러한 서비스를 제공하려면 Bluemix 계정이 필요합니다. 로그인 한 후, 위의 각 링크를 탐색하고 "서비스 생성"버튼을 선택하십시오.
106 |
107 | *서비스 생성*
108 |
109 |
110 |
111 |
112 | ### Watson Assistant
113 | [Watson Assistant](https://www.ibm.com/watson/developercloud/conversation.html) 서비스는 자연어를 분석하고 사용자 입력을 기반으로 수행할 액션을 결정합니다. 여기에는 두 가지 주요 개념이 있습니다. 첫째는 "Intents"(의도)인데, 이는 사용자가 응용 프로그램을 수행할 내용을 결정합니다. 둘째는 "Entities"인데 intents(의도)가 어디에 적용되어야 하는지에 대한 컨텍스트를 제공합니다. 이 예제에서는 간단한 두 가지 intents(의도)가 있습니다. 하나는 "끄다"이고, 다른 하나는 "켜다"입니다. 그리고 3 개의 entities(엔티티)를 가지고 있습니다. 이 경우 엔티티는 켜고 끄기를 원하는 가정용 디바이스입니다. 이 사전교육된 데이터 모델은 UI를 통해 프로비저닝된 Coversation 서비스에 업로드할 수 있습니다. 업로드를 시작하려면 Bluemix에 로그인하십시오. 그런 다음 대화 서비스를 선택한 다음 "Launch Tool"버튼을 선택하십시오.
114 |
115 | ### Watson IoT Platform
116 | Watson IoT Platform은 MQTT 메시징 브로커로 활용됩니다. 이것은 전화, 랩탑 및 마이크와 같은 다양한 장치가 Raspberry Pi와 통신할 수 있게 해주는 publish/subscribe 메시징 프로토콜입니다. 이 서비스가 프로비저닝되면 MQTT 브로커에 안전하게 액세스하기 위한 신임정보를 생성해야 합니다. 이러한 단계는 [여기](./ iot-gateway /) 에서 확인하실 수 있습니다.
117 |
118 | ### IBM Cloud Functions
119 | Raspberry Pi에서 파이프라인 및 복잡한 자동화 로직을 작성하고 실행하는 대신 서버리스 이벤트 기반 플랫폼인 [IBM Cloud Functions](https://console.ng.bluemix.net/openwhisk) 를 활용할 것입니다. IBM Cloud Functions 액션은 결과를 MQTT 메시지로 Raspberry Pi에 전달합니다. IBM Cloud Functions는 코드 스니펫을 REST API 엔드 포인트에 바인딩할 수 있는 서버리스 프레임워크입니다. 일단 작성되면 인터넷에 연결된 모든 디바이스에서 직접 실행하거나 데이터베이스 변경 또는 특정 MQTT 채널로 들어오는 메시지와 같은 이벤트에 응답할 수 있습니다. 이러한 스니펫 또는 "액션"이 생성되면 위의 아키텍처 다이어그램과 같이 시퀀스로 함께 연결될 수 있습니다.
120 |
121 | 우선, 세 가지 액션으로 구성된 시퀀스를 만듭니다. 첫 번째 액션은 오디오 페이로드를 텍스트로 변환합니다. 두 번째 액션은 Watson Assistant 서비스를 사용하여 문자 메시지 결과를 분석합니다. 이 분석은 음성 메시지의 의도를 추출하고, Raspberry Pi가 무엇을 하길 사용자가 원한 것인지에 대해 결정합니다. 예를 들어, 사용자가 "전등 켜기"또는 "스위치 누르기"의 행을 따라 뭔가를 말하면 NLC 서비스가 이를 해석할 수 있습니다. 마지막으로, 세 번째 액션은 MQTT 메시지를 보내서 Raspberry Pi에게 소켓을 켜고 끌 것을 알립니다
122 |
123 | Speech to text 액션은 OpenWisk에 공개 패키지로 이미 들어 있으므로 해당 서비스에 대한 신임정보를 제공하면 됩니다. 향후에는 다음 명령을 사용하여 추가 작업을 진행할 수 있습니다.
124 |
125 | ```
126 | cd serverless-home-automation/iot_gateway/whisk_actions
127 | wsk action create conversation conversation.js
128 | wsk action create iot-pub iot-pub.py
129 | ```
130 |
131 | 액션이 성공적으로 수행되면 각 작업에 대한 기본 서비스 신임정보를 설정할 수 있습니다. 그렇지 않으면, 액션에서 Watson 서비스를 호출할 때마다 서비스 신임정보를 전달해야 합니다. 이러한 신임정보를 얻으려면 Bluemix 대시보드에서 제공된 각 서비스를 클릭한 다음 "신임정보 보기"를 클릭하십시오.
132 |
133 |
134 |
135 |
136 |
137 | 그런 다음 아래 명령을 실행할 때 해당 신임정보를 넣으십시오.
138 |
139 | ```
140 | wsk action update conversation -p username ${conversation_username} -p password ${conversation_password} -p workspace_id ${conversation_workspace_id}
141 | wsk action update iot-pub -p org ${iot_org_id} -p device_id ${device_id} -p api_token ${api_token}
142 | wsk package bind /whisk.system/watson-speechToText myWatsonSpeechToText -p username ${stt_username} -p password ${stt_password}
143 | ```
144 |
145 | 다음으로, 액션을 시퀀스로 배열할 수 있습니다.
146 | ```
147 | wsk action create homeSequence --sequence myWatsonSpeechToText/speechToText,conversation,iot-pub
148 | ```
149 |
150 |
151 | 시퀀스가 Raspberry Pi에 결과를 리턴할 수 있으려면 MQTT 클라이언트가 Watson IoT 서비스에 등록(subscribe)되어야 합니다. / etc / environment 파일에 적절한 값이 설정되어 있으면 다음 명령을 실행하여 부팅할 때 자동으로 시작될 systemd 서비스를 만들고 활성화해야합니다. 그러면 Watson IoT Platform의 MQTT 브로커에 등록되고 intents와 엔티티 페어를 수신 대기하는 [노드 서버](./iot- gateway / node-mqtt.js)가 시작됩니다.
152 |
153 | ```
154 | sudo cp serverless-home-automation/iot-gateway/node-mqtt.service /etc/systemd/system/
155 | sudo systemctl enable node-mqtt
156 | sudo systemctl start node-mqtt
157 | sudo systemctl status node-mqtt
158 | ```
159 |
160 | ### Twilio
161 | Twilio는 개발자가 VoIP 및 SMS 기능을 플랫폼에 통합할 수 있게 하는 서비스입니다. 이는 개발자가 등록할 전화번호를 선택할 수 있게하여 작동합니다. Twilio는 일단 등록하면, API 엔드 포인트를 공개하여 전화와 문자를 프로그래밍 방식으로 만들 수 있도록 번호를 설정할 수 있습니다. 또한, 그 번호는 웹훅webhook을 트리거하거나 [Twiml](https://www.twilio.com/docs/api/twiml) 문서를 따라 수신되는 전화/텍스트에 응답하도록 설정할 수 있습니다. 이 경우, 이전 단계에서 만든 "homeSequence" IBM Cloud Functions 액션에 바인딩된 웹훅을 트리거하여 수신되는 텍스트에 응답하도록 Twilio 번호를 설정합니다. [IBM Cloud Functions 콘솔](https://console.bluemix.net/openwhisk/editor)로 이동하여 homeSequence 시퀀스를 선택한 다음 "View Action Details"버튼을 선택하여 웹훅에 대한 URL을 찾을 수 있습니다. 마지막으로 "Enable as Web Action"버튼을 선택하고 생성된 웹 액션 URL을 복사합니다.
162 |
163 | 우선, Twilio의 [등록 페이지](https://www.twilio.com/try-twilio) 를 방문하십시오. 가입한 후 로그인하여 메뉴에서 # 아이콘을 선택하면 브라우저가 [전화번호](https://www.twilio.com/console/phone-numbers/incoming) 설정페이지로 이동합니다. 이제 동그란 + 버튼을 선택하여 번호를 선택하고 등록하십시오. 등록한 후 번호를 클릭하여 구성하십시오. 아래로 스크롤하면 '메시징'섹션이 표시됩니다. "A Message Comes in"이라는 제목의 양식에서 아래 보이는 "homeSequence"IBM Cloud Functions 액션과 관련된 웹훅을 붙여 넣으십시오.
164 |
165 |
166 |
167 |
168 |
169 | ### Node Red
170 | IBM Cloud Functions에서 시퀀스를 생성하는 방법 대신, 홈오토메이션 로직을 [Node Red](https://github.com/node-red/node-red)를 사용하여 구성할 수 있습니다. Node Red는 사용자가 코드 또는 서비스 호출의 "블록"을 끌어다 놓고 연결할 수 있게함으로써 "흐름"을 조합할 수있는 비주얼 편집기입니다. 이 배포 방식은 노드 서버로 계속해서 실행되므로 서버리스 모델을 적용하지 않을 것입니다. 백엔드 로직은 모두 IBM Cloud Functions 서버리스 액션 풀에 있기 때문에 장기 실행(long running) 서버를 설정하지 않고도 SMS 또는 음성을 통해 디바이스를 제어할 수 있어야합니다. 그러나 Node Red를 사용하고 싶은 경우,`npm install node-red`를 통해 패키지를 설치하고,`node-red`를 통해 편집기를 부팅하고, 아래의 다이어그램과 같은 흐름을 생성할 수 있습니다. 흐름을 연결한 후에는, 각 블록에 인증 신임정보와 엔드포인트를 덧붙여야합니다.
171 |
172 | 
173 |
176 |
177 | Node Red 인스턴스를 Bluemix에 배포하려면, 아래 버튼을 클릭하십시오
178 |
179 | [](https://bluemix.net/deploy?repository=https://github.com/kkbankol-ibm/node-red-bluemix-starter.git)
180 |
181 | [Sample Bluemix Instance](http://serverless-home-automation.mybluemix.net/red/#flow/e711dbd4.4e7d18)
182 |
183 |
184 | ### 문제 해결
185 |
186 | RC 회로:
187 | 각각의 전선이 올바르게 정렬되었는지 확인한 후 [멀티미터](https://learn.sparkfun.com/tutorials/how-to-use-a-multimeter)를 사용하여 전원에서 시작하여 각 연결 노드를 점검하십시오. 예를 들어, RF 부품의 전원이 올바르게 공급되는지 확인하려면 멀티미터의 음극/접지 단자를 접지된 전원 레일에 연결하고 멀티미터의 양극 끝을 RF 부품 5V 핀에 연결하십시오.
188 |
189 | Bluemix 서비스:
190 | Bluemix 구성 요소(음성, 텍스트, 회화 등)가 응답하지 않는 것처럼 보이면 [Bluemix 상태 페이지](https://status.ng.bluemix.net/)에서 서비스가 다운되었거나 보수 중인지 확인하십시오. 그렇지 않다면 curl을 사용하여 샘플 요청을 하고 200 HTTP 응답이 리턴되는지 확인하십시오. speech-to-text서비스에 대한 샘플 요청은 아래 예시와 같습니다.
191 | ```
192 | curl -v -u ${username}:${password} https://stream.watsonplatform.net/speech-to-text/api/v1/models
193 | ```
194 |
195 | IBM Cloud Functions:
196 | `wsk -vvv action list` 를 보려면, 아무 wsk 명령에 -vv를 추가하십시오.
197 | 또한, [IBM Cloud Functions dashboard](https://console.bluemix.net/openwhisk/dashboard) 에서 활동 로그를 확인하십시오
198 |
199 | Raspberry Pi:
200 | `journalctl -ru node-mqtt`를 실행하여 Raspberry Pi의 노드 서버의 stdout 및 stderr 출력을 봅니다.
201 |
202 | Twilio:
203 | 수신/발송되는 SMS 메시지에 대한 출력을 보려면 [Twilio logging](https://www.twilio.com/console/sms/logs) 을 방문하십시오.
204 |
205 | # License
206 | [Apache 2.0](LICENSE)
207 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WARNING: This repository is no longer maintained :warning:
2 |
3 | > This repository will not be updated. The repository will be kept available in read-only mode.
4 |
5 | 
6 |
7 | ## Natural Language Interface based Home Automation
8 |
9 | *View this in other languages: [한국어](README-ko.md).*
10 |
11 | > Watson Conversation is now Watson Assistant. Although some images in this code pattern may show the service as Watson Conversation, the steps and processes will still work.
12 |
13 | ## Overview and goal
14 | Over the past few years, we’ve seen a significant rise in popularity for intelligent personal assistants, such as Apple’s Siri, Amazon Alexa, and Google Assistant. Though they initially appeared to be little more than a novelty, they’ve evolved to become rather useful as a convenient interface to interact with service APIs and IoT connected devices. This developer pattern will guide users through setting up their own starter home automation hub by using a Raspberry PI to turn power outlets off and on. Once the circuit and software dependencies are installed and configured properly, users will also be able to leverage Watson’s language services to control the power outlets via voice and/or text commands. Furthermore, we’ll show how IBM Cloud Functions serverless functions can be leveraged to trigger these sockets based on a timed schedule, changes to the weather, motion sensors being activated, etc.
15 |
16 | Audience level: Intermediate. User will need basic hardware skills to assemble electronic circuits on a breadboard, and be somewhat familiar with a Linux terminal to install Raspberry Pi dependencies.
17 |
18 | IBM Cloud Plans: This project will work with the Free/Lite version of all required services: Speech To Text, Text To Speech, Watson Assistant, and IBM Cloud Functions.
19 |
20 | Click here to view the [IBM Pattern](https://developer.ibm.com/code/patterns/implement-voice-controls-for-serverless-home-automation-hub/) for this project.
21 |
22 | ## Prerequisites
23 | You will need the following accounts and tools:
24 | * [IBM Cloud account](https://console.ng.bluemix.net/registration/)
25 | * [IBM Cloud CLI](https://console.bluemix.net/docs/cli/reference/bluemix_cli/all_versions.html#ibm-cloud-cli-installer-all-versions)
26 | * [Openwhisk CLI](https://github.com/apache/incubator-openwhisk-cli/releases)
27 |
28 | ### Architecture
29 |
30 | *Architecture flow*
31 | 1. User says a command into the microphone, or sends a text to the Twilio SMS number
32 | 2. User input is captured and embedded in an HTTP POST request triggering an IBM Cloud Functions sequence
33 | 3. The first IBM Cloud Functions action in the sequence forwards the audio to Speech to Text service, and waits for the response
34 | 4. Transcription is forwarded to the second IBM Cloud Functions action
35 | 5. IBM Cloud Functions action 2 calls the Watson Assistant service to analyze the user's text input, again waits for the response
36 | 6. Watson Assistant service result is forwarded to final IBM Cloud Functions action
37 | 7. Final IBM Cloud Functions action publishes a entity/intent pair (fan/turnon for example) to the IoT MQTT broker
38 | 8. MQTT client subscribed on Raspberry Pi receives and interprets result
39 | 9. Raspberry Pi transmits corresponding RF signal to adjust outlet state
40 |
41 |
42 | ## Steps
43 | - [Connect And Configure Hardware](#configure-hardware-components)
44 | * Assemble RF Circuit
45 | * Install Software Dependencies + Libraries
46 | * Capture RF codes corresponding to wireless sockets
47 | - [Provision IBM Cloud Services](#provision-and-configure-platform-services)
48 | - [Create Serverless Functions](ibm_cloud_functions)
49 | - [Deploy to IBM Cloud](#bluemix)
50 |
51 | * **Configure Hardware Components**
52 |
53 | We can get started by assembling and configuring the RF circuit. This circuit requires the following components
54 | - [Raspberry PI 3](https://www.raspberrypi.org/products/raspberry-pi-3-model-b/)
55 | - [GPIO Ribbon cable + Breakout Board](https://www.adafruit.com/product/914)
56 | - [433MHz RF transmitter and receiver](https://www.amazon.com/SMAKN%C2%AE-433Mhz-Transmitter-Receiver-Arduino/dp/B00M2CUALS)
57 | - [Etekcity 433 MHz Outlets](https://www.amazon.com/dp/B00DQELHBS)
58 | - [Electronic Breadboard](https://www.adafruit.com/product/239)
59 | - [USB Microphone](https://www.amazon.com/eBerry-Adjustable-Microphone-Compatible-Recording/dp/B00UZY2YQE/ref=sr_1_9?ie=UTF8&qid=1497828013&sr=8-9&keywords=usb+microphone)
60 |
61 | Once all components have been obtained, assemble them to form the circuit below. In this circuit, we have the Raspberry Pi connected to the electronic breadboard via the GPIO ribbon/breakout board.
62 |
63 | 
64 |
65 |
66 |
67 | The red wire just left of the breakout board is responsible for bridging 5 volts from the Raspberry Pi to one of the breadboard's power rails. The additional red wires to the bottom right of the diagram supply those 5 volts from the power rail to the RF receiver and transmitter. Similar concept for the white wires, except those provide a negative charge, commonly referenced to as "ground". Next, we have the green wire that connects the Raspberry Pi's GPIO pin 17 to the transmitter's data pin, and the black wire connects the GPIO pin 27 to the receiver's data pin. The reason for this can be seen in the `gpio readall` output in image below, as the transmitter defaults to [wiringPi pin 0](https://github.com/ninjablocks/433Utils/blob/master/RPi_utils/codesend.cpp#L27) which maps to BCM 17, and the receiver defaults to [wiringPi pin 2](https://github.com/ninjablocks/433Utils/blob/master/RPi_utils/RFSniffer.cpp#L25), which maps to BCM 27. These default pins can be changed by modifying either of the linked files in the 433Utils library, and recompiling the library.
68 |
69 | Once the Raspberry Pi is connected to the circuit, we'll need to install dependencies to allow us to interact with the RF transmitter and receiver. This can be accomplished by running the [install_deps.sh](./iot-gateway/install_deps.sh) script.
70 |
71 | The open source libraries that are being installed here are [wiringPi](http://wiringpi.com/) and [433Utils](https://github.com/ninjablocks/433Utils). wiringPi enables applications to read/control the Raspberry Pi’s GPIO pins. 433Utils calls the wiringPi library to transmit and receive messages via the 433MHz frequency. In our case, each outlet has a unique RF code to turn power on and off. We’ll use one of the wiringPi utilities, titled “RFSniffer” to essentially register each of these unique codes. The 433MHz frequency is standard among many common devices such as garage door openers, thermostats, window/door sensors, car keys, etc. So this initial setup is not limited to only controlling power outlets.
72 |
73 | Once the script completes run `gpio readall` to ensure that wiringPi installed successfully. The following chart should be displayed.
74 |
75 |
76 |
77 |
78 | Now we can determine which RF codes correspond with the Etekcity outlets. Start by executing
79 | ```
80 | sudo /opt/433Utils/RPi_utils/RFSniffer
81 | ```
82 |
83 | This will listen on the RF receiver for incoming signals, and write them to stdout. As the on/off buttons are pressed on the Etekcity remote, the Raspberry Pi should show the following output if the circuit is wired correctly.
84 | ```
85 | pi@raspberrypi:~ $ sudo /opt/433Utils/RPi_utils/RFSniffer
86 | Received 5528835
87 | Received pulse 190
88 | Received 5528844
89 | Received pulse 191
90 | ```
91 |
92 | After determining the on/off signal for the RF sockets, place the captured signals into the `/etc/environment` file like so.
93 | ```
94 | RF_PLUG_ON_1=5528835
95 | RF_PLUG_ON_PULSE_1=190
96 | RF_PLUG_OFF_1=5528844
97 | RF_PLUG_OFF_PULSE_1=191
98 | ```
99 |
100 | Now, plug in the associated socket, and run the following command to ensure the Raspberry Pi can turn the socket on and off. This command simply sends the RF code at the requested pulse length, which is to be provided as the `-l` parameter.
101 |
102 | ```
103 | source /etc/environment
104 | /opt/433Utils/RPi_utils/codesend ${RF_PLUG_ON_1} -l ${RF_PLUG_ON_PULSE_1}
105 | /opt/433Utils/RPi_utils/codesend ${RF_PLUG_OFF_1} -l ${RF_PLUG_OFF_PULSE_1}
106 | ```
107 |
108 | Now that we can control the sockets manually via cli, we’ll move forward and experiment with different ways to control them in an automated fashion. Rather than writing and executing pipelines and complex automation logic on the Raspberry Pi, we’ll utilize a serverless, event driven platform called IBM Cloud Functions. In this implementation, IBM Cloud Functions actions communicate with the Raspberry Pi via MQTT messages.
109 |
110 | * **Audio Interface**
111 |
112 | Once the Raspberry Pi is setup, we'll need to configure it to recognize audio input from the USB microphone. To ensure that audio is recorded and transcribed only as needed, we'll leverage a "Hotword" detection service named [Snowboy](https://snowboy.kitt.ai/), which listens for a specific speech pattern (**Hello Watson**, in this case), and begins recording once the hotword pattern is detected. The steps required to create a voice model can be found [here](http://docs.kitt.ai/snowboy/).
113 |
114 | * **Provision and Configure Platform Services**
115 |
116 | - [Watson Assistant](https://console.bluemix.net/catalog/services/conversation)
117 | - [Speech to Text](https://console.bluemix.net/catalog/services/speech-to-text)
118 | - [Watson IoT Platform](https://console.bluemix.net/catalog/services/internet-of-things-platform)
119 | - [Twilio](https://console.bluemix.net/catalog/services/twilio)
120 |
121 |
122 | A IBM Cloud Account is required to provision these services. After logging in, simply navigate to each of the links above, and select the `Create Service` button.
123 |
124 | *Create Service*
125 |
126 |
127 |
128 |
129 | * **Watson Assistant**
130 |
131 | The [Watson Assistant](https://www.ibm.com/watson/developercloud/conversation.html) service is used to analyze natural language and determine which action(s) to take based on the user input. There are two main concepts to understand here. The first are referred to as "Intents", which determine what the user would like the application to do. Next, we have "Entities", which provide context of where the intent should be applied. To keep things simple, we have two intents, one is titled "turnoff", the other "turnon". Next, we have 3 entities, which are household devices that we'd like to turn off and on in this case. This pre-trained data model can be uploaded to the provisioned Watson Assistant service through the UI. To initiate the upload, login to the IBM Cloud console. Next select the `Watson Assistant` service, and then the button titled `Launch Tool`.
132 |
133 | * **Watson IoT Platform**
134 |
135 | The Watson IoT Platform will be utilized as a MQTT messaging broker. This is a lightweight publish/subscribe messaging protocol that'll allow for various devices such as a Phone, Laptop, and Microphone to communicate with the Raspberry Pi. Once this service has been provisioned, we'll need to generate a set of credentials to securely access the MQTT broker. These steps are listed [here](./iot-gateway/)
136 |
137 | * **IBM Cloud Functions**
138 |
139 | Rather than writing and executing pipelines and complex automation logic on the Raspberry Pi, we’ll utilize a serverless, event driven platform called [IBM Cloud Functions](https://console.ng.bluemix.net/openwhisk). In this implementation, IBM Cloud Functions actions forward their results to the Raspberry Pi as MQTT messages. IBM Cloud Functions is a serverless framework which has the ability to bind snippets of code to REST API endpoints. Once these have been created, they can be executed directly from any internet connected device, or they can respond to events such as a database change or a message coming in to a specific MQTT channel. Once these snippets, or "Actions" have been created, they may be chained together as a sequence, as seen above in the architecture diagram.
140 |
141 | To get started, we will create a sequence that consists of three actions. The first action will transcribe an audio payload to text. The second action will analyze the transcribed text result using the Watson Assistant service. This analysis will extract the intent behind the spoken message, and determine what the user would like the Raspberry Pi to do. So, for example, if the user says something along the line of “Turn on the light” or “Flip the switch”, the NLC service will be able to interpret that. Finally, the third action will send a MQTT message that’ll notify the Raspberry Pi to switch the socket on/off.
142 |
143 | The speech to text action is already built in to IBM Cloud Functions as a public package, so we’ll just need to supply our credentials for that service. Moving forward, we can create the additional actions with the following commands.
144 |
145 | ```
146 | cd serverless-home-automation/ibm_cloud_functions
147 | wsk action create conversation conversation.js
148 | wsk action create iot-pub iot-pub.py
149 | ```
150 |
151 | Once the actions are successfully created, we can set default service credentials for each of the actions. Otherwise we’d have to pass in the service credentials every time we’d like our actions to call the Watson services. To obtain these credentials, click each provisioned service in the IBM Cloud dashboard, and then select the `View credentials` dropdown.
152 |
153 | 
154 |
155 | Then insert the corresponding credentials when running the commands below.
156 |
157 | * If the service credentials from IBM Watson Assistant are username/password based as shown in the diagram below, populate the username, password and workspace_id fields and comment out the IAM credentials fields.
158 |
159 | 
160 |
161 |
162 | ```
163 | wsk action update conversation -p username ${conversation_username} -p password ${conversation_password} -p workspace_id ${conversation_workspace_id}
164 | wsk action update iot-pub -p iot_org_id ${iot_org_id} -p device_id ${device_id} -p api_token ${api_token} -p device_type ${device_type}
165 | wsk package bind /whisk.system/watson-speechToText myWatsonSpeechToText -p username ${stt_username} -p password ${stt_password}
166 | ```
167 |
168 | * If the service credentials from IBM Watson Assistant are IAM based as shown below in the diagram, populate the IAM apikey, url, and workspace_id fields and comment out the username/password fields
169 |
170 | 
171 |
172 |
173 | ```
174 | wsk action update conversation -p iamApiKey ${apikey} -p workspace_id ${conversation_workspace_id}
175 | wsk action update iot-pub -p iot_org_id ${iot_org_id} -p device_id ${device_id} -p api_token ${api_token} -p device_type ${device_type}
176 | wsk package bind /whisk.system/watson-speechToText myWatsonSpeechToText -p username ${stt_username} -p password ${stt_password}
177 | ```
178 |
179 |
180 | Next, we can arrange the actions into a sequence
181 | ```
182 | wsk action create homeSequence --sequence myWatsonSpeechToText/speechToText,conversation,iot-pub
183 | ```
184 |
185 |
186 | For the sequence to be able to return the result to the Raspberry Pi, a MQTT client will need to be listening to the Watson IoT service. If the proper values have been set in the `/etc/environment` file, you should just have to run the following commands to create and enable a systemd service, which will automatically start on boot. This will start the [node server](./iot-gateway/node-mqtt.js), which subscribes to the Watson IoT Platform's MQTT broker and listens for intent entity pairs.
187 |
188 | ```
189 | sudo cp serverless-home-automation/iot-gateway/node-mqtt.service /etc/systemd/system/
190 | sudo systemctl enable node-mqtt
191 | sudo systemctl start node-mqtt
192 | sudo systemctl status node-mqtt
193 | ```
194 |
195 | * **Twilio**
196 |
197 | Twilio is a service that enables developers to integrate VoIP and SMS capabilities into their platform. This works by allowing developers to choose a phone number to register. Once registered, Twilio exposes an API endpoint to allow calls and texts to be made programmatically from the number. Also, the number can be configured to respond to incoming calls/texts by either triggering a webhook or following a [Twiml](https://www.twilio.com/docs/api/twiml) document. In this case, we'll configure the Twilio number to respond to incoming texts by triggering a webhook bound to the "homeSequence" IBM Cloud Functions action we created in the previous step. We can find the url to the webhook by navigating to the [IBM Cloud Functions console](https://console.bluemix.net/openwhisk/editor), selecting the homeSequence sequence, and then selecting the `View Action Details` button. Finally, check the `Enable as Web Action` button, and copy the generated Web Action URL.
198 |
199 | To get started, please visit Twilio's registration [page](https://www.twilio.com/try-twilio). After signing up, log in and select the `#` icon in the menu, which will direct the browser to the [Phone Numbers](https://www.twilio.com/console/phone-numbers/incoming) configuration. Now, select the circular `+` button to select and register a number. After registration, click the number to configure it. Scrolling down will reveal a `Messaging` section. In the form titled `A Message Comes in`, paste the webhook associated with the "homeSequence" IBM Cloud Functions action, as seen below.
200 |
201 |
202 |
203 |
204 |
205 | * **Node Red**
206 |
207 | As an alternative to creating sequences in IBM Cloud Functions, the home automation logic can be arranged using [Node Red](https://github.com/node-red/node-red). Node Red is a visual editor capable of assembling "flows", which is done by allowing users to drag, drop and connect "blocks" of code or service calls. It's worth noting that this deployment scheme won't follow a fully serverless model, as it'll be running constantly as a node server. Since the backend logic is all in the IBM Cloud Functions serverless action pool, the devices should be able to be controlled via SMS or voice without having to set up a long running server. However, in use cases where it's preferable to use node red, we can do so by installing the package via `npm install node-red`, booting up the editor via `node-red`, and creating a flow like what we have in the diagram below. After assembling the flow, be sure to populate the authentication credentials and endpoint for each block.
208 |
209 | 
210 |
211 | To deploy a node red instance to IBM Cloud, click the button below
212 |
213 | [](https://bluemix.net/deploy?repository=https://github.com/kkbankol-ibm/node-red-bluemix-starter.git)
214 |
215 | [Sample IBM Cloud Instance](http://serverless-home-automation.mybluemix.net/red/#flow/e711dbd4.4e7d18)
216 |
217 |
218 | * **Troubleshooting**
219 |
220 | RF Circuit:
221 | After checking each of the wires to ensure they are lined up correctly, use a [multimeter](https://learn.sparkfun.com/tutorials/how-to-use-a-multimeter) to check each of the connection nodes starting from the power source. For example, to ensure that RF components are being powered properly, touch the negative/grounded end of the multimeter to the grounded power rail, and touch the positive end of the multimeter to the RF components 5V pin.
222 |
223 | Audio:
224 | Jack Server
225 |
226 | ```
227 | # jack server is not running or cannot be started
228 |
229 | DISPLAY=:0 jack_control start
230 | pulseaudio --start
231 | ```
232 |
233 | IBM Cloud Services:
234 | Whenever any of the IBM Cloud components (Speech to Text, Assistant, etc) seem to be unresponsive, check the [IBM Cloud Status page](https://status.ng.bluemix.net/) to see if the service is down or under maintenance. If not, try running a sample request using curl and ensure that a 200 HTTP response is returned. A sample request against the speech-to-text service would look like so.
235 | ```
236 | curl -v -u ${username}:${password} https://stream.watsonplatform.net/speech-to-text/api/v1/models
237 | ```
238 |
239 | IBM Cloud Functions:
240 | Add `-vv` to any wsk command `wsk -vvv action list` to view the
241 | Also, check the activity log in the [IBM Cloud Functions dashboard](https://console.bluemix.net/openwhisk/dashboard)
242 |
243 | Raspberry Pi:
244 | Run `journalctl -ru node-mqtt` to view the stdout and stderr output of the Raspberry Pi's node server
245 |
246 | Twilio:
247 | Visit the [Twilio logging](https://www.twilio.com/console/sms/logs) url to view output for incoming and outgoing SMS messages
248 |
249 |
250 | ## Useful links
251 |
252 | * [IBM Cloud](https://bluemix.net/)
253 | * [IBM Cloud Documentation](https://www.ng.bluemix.net/docs/)
254 | * [IBM Cloud Developers Community](http://developer.ibm.com/bluemix)
255 | * [IBM Watson Internet of Things](http://www.ibm.com/internet-of-things/)
256 | * [IBM Watson IoT Platform](http://www.ibm.com/internet-of-things/iot-solutions/watson-iot-platform/)
257 | * [IBM Watson IoT Platform Developers Community](https://developer.ibm.com/iotplatform/)
258 | * [Simulate IoT Device](https://github.com/IBM/manage-control-device-node-red)
259 | * [Node-RED](https://nodered.org/)
260 |
261 | ## License
262 |
263 | This code pattern is licensed under the Apache Software License, Version 2. Separate third party code objects invoked within this code pattern are licensed by their respective providers pursuant to their own separate licenses. Contributions are subject to the [Developer Certificate of Origin, Version 1.1 (DCO)](https://developercertificate.org/) and the [Apache Software License, Version 2](http://www.apache.org/licenses/LICENSE-2.0.txt).
264 |
265 | [Apache Software License (ASL) FAQ](http://www.apache.org/foundation/license-faq.html#WhatDoesItMEAN)
266 |
--------------------------------------------------------------------------------
/audio/.asoundrc:
--------------------------------------------------------------------------------
1 | pcm.!default {
2 | type hw card 1
3 | }
4 | ctl.!default {
5 | type hw card 1
6 | }
7 |
--------------------------------------------------------------------------------
/audio/.asoundrc.matrix:
--------------------------------------------------------------------------------
1 | pcm.!default {
2 | type asym
3 | capture.pcm {
4 | type plug
5 | slave.pcm "mic_channel8"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/audio/README-ko.md:
--------------------------------------------------------------------------------
1 | ## Raspberry Pi를 이용하는 오디오 인터페이스
2 |
3 | *다른 언어로 보기: [English](README.md).*
4 |
5 | ### 준비 사항
6 | - Raspbian OS 기반의 Raspberry Pi 3
7 | - 일반 USB Microphone
8 |
9 | ### Hotword 생성하기
10 | Speech to text를 통해 글로 옮긴 내용을 트리거하기 위해 맞춤 웨이크워드(wakeword, 깨우는 말)를 만들어 보겠습니다. 예를 들어 ** Hey Watson ** 또는 ** Hello Watson ** 또는 ** Hi Watson **과 같은 웨이크워드를 듣는 핫워드 엔진을 만들 수 있습니다. 일단 오디오 인터페이스를 통해 웨이크 워드를 감지하면 후속 오디오를 사용하여 텍스트화할 수 있습니다.
11 |
12 | 이러한 목적으로 Snowboy를 사용했습니다. 먼저 [Snowboy 웹사이트](https://snowboy.kitt.ai/) 에서 hotword를 생성합니다. Hotword를 만들려면 Github, Google 또는 Facebook으로 로그인해야합니다.
13 |
14 | 세 가지 오디오 샘플로 된 hotword를 만들었으면 이제 다음 섹션에서 사용할 모델을 다운로드 할 수 있습니다
15 |
16 | ### 마이크 설정하기
17 | 여러분의 마이크를 raspberry Pi에 설정하시려면 다음의 [링크](http://docs.kitt.ai/snowboy/#running-on-raspberry-pi)를 이용하십시오.
18 |
19 |
20 | ### 데모 실행하기
21 | 이제 hotword가 감지된 후 오디오를 캡처하고 캡처된 오디오와 함께 Watson Speech to Text를 사용하는 샘플 스크립트를 실행해 봅니다.
22 |
23 | 먼저, [Raspbian 8.0이 포함된 Raspberry Pi](https://s3-us-west-2.amazonaws.com/snowboy/snowboy-releases/rpi-arm-raspbian-8.0-1.1.0.tar.bz2) 를 위해 미리 패키지 된 Snowboy 바이너리와 Python wrapper를 다운로드하여 압축을 풉니다.
24 |
25 | 압축을 푼 폴더에 [detect_hotword.py](audio_interface/detect_hotword.py) & [record_audio.py](audio_interface/record_audio.py) 를 복사하십시오.
26 |
27 |
28 |
--------------------------------------------------------------------------------
/audio/README.md:
--------------------------------------------------------------------------------
1 | ## Raspberry Pi powered Audio Interface
2 | *Read this in other languages: [한국어](README-ko.md).*
3 |
4 | ### Requirements
5 | - Raspberry Pi 3 with Raspbian OS
6 | - Generic USB Microphone
7 |
8 | ### Configure Hotword using Snowboy service
9 | Let's start with creating custom wakeword in order to trigger speec to text transcription. For example, we can create a hotword engine that would listen on wake word such as **Hey Watson** or **Hello Watson** or **Hi Watson** once it detects the wake word through audio interface, we can then use the subsequent audio for transcription.
10 |
11 | We used Snowboy for this purpose. First create a hotword from their official [website](https://snowboy.kitt.ai/).
12 |
13 | Once you create a hotword with three audio samples, you can now download the model which we will use in the later section.
14 |
15 | ### Configure Microphone
16 | After plugging in the microphone, obtain the sound card number with the following command
17 | ```
18 | pi@raspberrypi2:/tmp $ cat /proc/asound/cards
19 | 0 [ALSA ]: bcm2835 - bcm2835 ALSA
20 | bcm2835 ALSA
21 | 1 [Dummy ]: Dummy - Dummy
22 | Dummy 1
23 | 2 [Device ]: USB-Audio - USB PnP Sound Device
24 | C-Media Electronics Inc. USB PnP Sound Device at usb-3f980000.usb-1.3, full spe
25 | ```
26 |
27 | We see that our USB-Audio is card 2, so we'll need to reflect that in our ~/.asoundrc file
28 | ```
29 | pi@raspberrypi2:/tmp $ cat ~/.asoundrc
30 | pcm.!default {
31 | type hw card 2
32 | }
33 | ctl.!default {
34 | type hw card 2
35 | }
36 | ```
37 |
38 |
39 | Check if audio can be recorded via microphone using
40 | `rec /tmp/temp.wav`
41 |
42 | Press Ctrl+C to quit the `rec` process
43 |
44 | ### Running a demo
45 | Now we will run the sample script that will capture the audio after the hotword is detected and use Watson Speech to Text with the captured audio.
46 | To do so, run the following to clone the associated "snowboy" submodule.
47 | ```
48 | git submodule init
49 | git submodule update
50 | ```
51 |
52 | Test end-to-end sequence with the following command
53 | ```
54 | python demo_arecord.py Hey_Watson_PI.pmdl
55 | ```
56 |
57 | When the wakeword is detected, the `wsk_transcribe_audio` method defined [here](https://github.com/kkbankol-ibm/snowboy/blob/26f49a6a12088f2c2797d68db5ef7eda88798deb/examples/Python/snowboydecoder.py#L61:L77) will record audio for 3 seconds, convert the wav result to a smaller, lossless flac audio file, and then forward the flac audio to the openwhisk sequence. The `whisk_namespace`, `whisk_action`, and `auth` values in the method will all need to be updated with your personal account. These values can be found by running `wsk property get` and `wsk action list`.
58 |
59 | ### Troubleshooting
60 | ```
61 | # jack server is not running or cannot be started
62 |
63 | DISPLAY=:0 jack_control start
64 | pulseaudio --start
65 | ```
66 |
67 | Suppress false errors
68 | https://github.com/Kitt-AI/snowboy/issues/9
69 |
--------------------------------------------------------------------------------
/audio/install_matrix_deps.sh:
--------------------------------------------------------------------------------
1 | # Install dependencies
2 | sudo apt-get -y update
3 | sudo apt-get -y upgrade
4 | sudo apt-get -y install build-essential git-core sox alsa-utils curl wget cmake g++ git libfftw3-dev wiringpi libgflags-dev
5 |
6 | # Update .asoundrc file
7 | cp ~/.asoundrc ~/.asoundrc.bak
8 | wget https://raw.githubusercontent.com/IBM/serverless-home-automation/master/audio/.asoundrc.matrix -P ~/ #.asoundrc
9 | mv ~/.asoundrc.matrix ~/.asoundrc
10 |
11 |
12 | # Clone matrix repository
13 | git clone https://github.com/matrix-io/matrix-creator-hal.git
14 |
15 | # Build matrix demos
16 | cd matrix-creator-hal
17 | mkdir build && cd build
18 | cmake ..
19 | make
20 |
21 | # Test mic array demo
22 | echo "Recording voice"
23 | ~/matrix-creator-hal/build/demos/micarray_recorder
24 | echo "Voice recording complete"
25 |
26 | # Convert beamformed result
27 | sox -r 16000 -c 1 -e signed -c 1 -e signed -b 16 mic_16000_s16le_channel_8.raw beamforming_result.wav
28 |
29 | # Transcribe result using Watson
30 | curl -u "{username}":"{password}" --header "Content-Type: audio/wav" --data-binary "@beamforming_result.wav" "https://stream.watsonplatform.net/speech-to-text/api/v1/recognize
31 |
--------------------------------------------------------------------------------
/audio/requirements.txt:
--------------------------------------------------------------------------------
1 | PyAudio==0.2.9
2 | snowboy
3 |
--------------------------------------------------------------------------------
/ibm_cloud_functions/clock.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * main() will be invoked when you Run This Action.
4 | *
5 | * @param Cloud Functions actions accept a single parameter,
6 | * which must be a JSON object.
7 | *
8 | * In this case, the params variable will look like:
9 | * { "message": "xxxx" }
10 | *
11 | * @return which must be a JSON object.
12 | * It will be the output of this action.
13 | *
14 | */
15 |
16 | // Beforehand run both
17 | // wsk trigger create timer
18 | // wsk trigger create alarm
19 | function main(params) {
20 | return new Promise(function(resolve, reject) {
21 |
22 | var openwhisk = require('openwhisk');
23 | const options = {apihost: 'openwhisk.ng.bluemix.net', api_key: ''};
24 | const ow = openwhisk(options);
25 |
26 | // expecting following in params
27 | // params['sys-time'] = "00:05:00"
28 | // params['sys-date'] = "2017-11-13"
29 | // params['type'] = "timer" // (optional, used to tell difference between 5 minute timer and alarm at 5:00 am, both read as 05:00:00)
30 | // # ┌───────────── minute (0 - 59)
31 | // # │ ┌───────────── hour (0 - 23)
32 | // # │ │ ┌───────────── day of month (1 - 31)
33 | // # │ │ │ ┌───────────── month (0 - 11)
34 | // # │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday)
35 | // # │ │ │ │ │
36 | // # │ │ │ │ │
37 | // # 0 0 1 0 *
38 | // first value is optional, references seconds
39 | // */20 * * * * * // every 20 seconds
40 |
41 | // var params = {'sys-time': '00:00:05', 'sys-date': '2017-11-13', 'clock': 'timer'} // 5 second timer, for testing
42 | var time = inputParams['sys-time'].split(':')
43 | var date = inputParams['sys-date'].split('-')
44 |
45 | var zero = function(num) {
46 | return ((Boolean(Number(num))) ? ("*/" + Number(num)) : "*")
47 | }
48 |
49 | var genCron = function() {
50 | if (params.clock.includes("timer")) {
51 | var cron = (Boolean(Number(time[2])) ? (zero(time[2])) : "") + " " + zero(time[1]) + " " + zero(time[0]) + " " + "* * *"
52 | }
53 | else if (params.clock.includes("alarm")) {
54 | var cron = time[1] + " " + time[0] + " " + date[2] + " " + date[1] + " " + "* *"
55 | }
56 | return cron
57 | }
58 |
59 | // ow.actions.invoke({actionName: params.intents[0]['intent'], params: {entites: "params.entities"}}).then(
60 | // console.log("alarm set")
61 | // )
62 | //var feedParams = {cron: genCron(), maxTriggers: 1, trigger_payload: {message: params['type'] + " set for " + params['sys-time'] }}
63 |
64 | var feedParams = {
65 | cron: genCron(),
66 | trigger_payload: {message: params['clock'] + " set for " + params['sys-time'] },
67 | maxTriggers: 1
68 | }
69 | var name = '/whisk.system/alarms/alarm'
70 | var trigger = params.clock + '_' + time.join("") + '_' + date.join("")
71 | ow.triggers.create({name: trigger, params: {maxTriggers: 1 }})
72 | ow.feeds.create({name, trigger, params: feedParams }).then(package => {
73 | console.log('alarm trigger feed created', package)
74 | return {"payload": "alarm for " + JSON.stringify(params) + " set"}
75 | }).catch(err => {
76 | console.error('failed to create alarm trigger', err)
77 | })
78 |
79 | // ow.triggers.create({
80 | // name: "alarm1",
81 | // feed: "/whisk.system/alarms/alarm"
82 | // })
83 |
84 |
85 | // return Promise.all(actions).then(function (results) {
86 | // console.log(results);
87 | // return resolve({payload: "All OK"});
88 | // });
89 | });
90 | }
91 |
--------------------------------------------------------------------------------
/ibm_cloud_functions/conversation.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * main() will be invoked when you Run This Action.
4 | *
5 | * @param OpenWhisk actions accept a single parameter,
6 | * which must be a JSON object.
7 | *
8 | * In this case, the params variable will look like:
9 | * { "message": "xxxx" }
10 | *
11 | * @return which must be a JSON object.
12 | * It will be the output of this action.
13 | *
14 | */
15 | var request = require('request');
16 |
17 | function main(params) {
18 | var username = params.username
19 | var password = params.password
20 | var iamApiKey = params.iamApiKey
21 | var workspace_id = params.workspace_id
22 | var auth = {"user": username,"pass":password}
23 | var url = "https://gateway.watsonplatform.net/assistant/api/v1/workspaces/" + workspace_id + "/message?version=2018-07-10"
24 | if(iamApiKey){
25 | auth = {"user": "apikey","pass":iamApiKey}
26 | url = "https://gateway-wdc.watsonplatform.net/assistant/api/v1/workspaces/" + workspace_id + "/message?version=2018-07-10"
27 | }
28 | var input_text = params.data
29 | var body = {"input": {"text": input_text}}
30 | return new Promise(function(resolve, reject) {
31 | request( {
32 | url: url,
33 | method: 'POST',
34 | auth: auth,
35 | headers: {
36 | "content-type": "application/json",
37 | },
38 | body: JSON.stringify({"input": {
39 | "text": input_text
40 | }})
41 | },
42 | function(error, response, body) {
43 | if (error) {
44 | reject(error);
45 | }
46 | else {
47 | var output = JSON.parse(body)
48 | resolve({msg: output});
49 | }
50 | });
51 | }
52 | );
53 |
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/ibm_cloud_functions/iot-pub.py:
--------------------------------------------------------------------------------
1 | # This function publishes the results from the Watson Assistant service to the Watson IoT platform
2 | import requests
3 | def main(iot_obj):
4 | # import IOT platform credentials
5 | iot_org_id = iot_obj['iot_org_id']
6 | device_id = iot_obj['device_id']
7 | device_type = iot_obj['device_type']
8 | api_token = iot_obj['api_token']
9 | # extract result from Watson Assistant
10 | payload = {"d": iot_obj['msg']}
11 | # publish Watson Assistant intent/entity pair to Watson IOT platform and subsequently all subscribed devices
12 | requests.post('https://' + iot_org_id + '.messaging.internetofthings.ibmcloud.com:8883/api/v0002/device/types/' + device_type + '/devices/' + device_id + '/events/query', headers={'Content-Type': 'application/json'}, json=payload, auth=('use-token-auth', api_token))
13 | return {"msg": payload}
14 |
--------------------------------------------------------------------------------
/ibm_cloud_functions/nlc.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * main() will be invoked when you Run This Action.
4 | *
5 | * @param OpenWhisk actions accept a single parameter,
6 | * which must be a JSON object.
7 | *
8 | * In this case, the params variable will look like:
9 | * { "message": "xxxx" }
10 | *
11 | * @return which must be a JSON object.
12 | * It will be the output of this action.
13 | *
14 | */
15 | var request = require('request');
16 |
17 | function main(params) {
18 | var username = params.username
19 | var password = params.password
20 | var workspace_id = params.workspace_id
21 | var input_text = params.data
22 | var url = "https://gateway.watsonplatform.net/conversation/api/v1/workspaces/" + workspace_id + "/message?version=2017-04-21"
23 | var body = {"input": {"text": input_text}}
24 | return new Promise(function(resolve, reject) {
25 | request( {
26 | url: url,
27 | method: 'POST',
28 | auth: {
29 | 'user': username,
30 | 'pass': password
31 | },
32 | headers: {
33 | "content-type": "application/json",
34 | },
35 | body: JSON.stringify({"input": {
36 | "text": input_text
37 | }})
38 | },
39 | function(error, response, body) {
40 | if (error) {
41 | reject(error);
42 | }
43 | else {
44 | var output = JSON.parse(body)
45 | resolve({msg: output});
46 | }
47 | });
48 | }
49 | );
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/ibm_cloud_functions/requirements.txt:
--------------------------------------------------------------------------------
1 | requests
2 |
--------------------------------------------------------------------------------
/ibm_cloud_services/conversation/conversation.json:
--------------------------------------------------------------------------------
1 | {"name":"home","intents":[{"intent":"stream_media","examples":[{"text":"watch stranger things on netflix"},{"text":"play song by"},{"text":"i want to listen to"},{"text":"turn on the news"},{"text":"i want to watch"},{"text":"change the channel to"}],"description":null},{"intent":"play_music","examples":[{"text":"play song by"},{"text":"listen to"},{"text":"play the album"},{"text":"i want to listen to"}],"description":null},{"intent":"turnon","examples":[{"text":"turn on the"},{"text":"switch on"}],"description":null},{"intent":"clock","examples":[{"text":"set alarm for"},{"text":"set a alarm for"},{"text":"set timer for"},{"text":"set a timer"},{"text":"set alarm"},{"text":"wake me up at"},{"text":"wake me up in the morning"}],"description":null},{"intent":"query","examples":[{"text":"what's the weather like outside"},{"text":"what are the directions to"},{"text":"what is"},{"text":"where can i find"}],"description":null},{"intent":"turnoff","examples":[{"text":"turn off the"},{"text":"switch off"},{"text":"cut off"}],"description":null}],"entities":[{"entity":"media_platforms","values":[{"type":"synonyms","value":"soundcloud","metadata":null,"synonyms":[]},{"type":"synonyms","value":"spotify","metadata":null,"synonyms":[]},{"type":"synonyms","value":"hulu","metadata":null,"synonyms":[]},{"type":"synonyms","value":"netflix","metadata":null,"synonyms":[]},{"type":"synonyms","value":"youtube","metadata":null,"synonyms":[]}],"metadata":null,"description":null},{"entity":"sys-percentage","values":[],"metadata":null,"description":null},{"entity":"state","values":[{"type":"synonyms","value":"on","metadata":null,"synonyms":[]},{"type":"synonyms","value":"off","metadata":null,"synonyms":[]},{"type":"synonyms","value":"increase","metadata":null,"synonyms":["higher","raise","lift"]},{"type":"synonyms","value":"channel","metadata":null,"synonyms":[]},{"type":"synonyms","value":"video_mode","metadata":null,"synonyms":[]},{"type":"synonyms","value":"playing","metadata":null,"synonyms":["streaming"]},{"type":"synonyms","value":"paused","metadata":null,"synonyms":["stopped"]},{"type":"synonyms","value":"decrease","metadata":null,"synonyms":["lower","bring down"]}],"metadata":null,"description":null},{"entity":"monitor","values":[],"metadata":null,"description":null},{"entity":"media","values":[{"type":"patterns","value":"show_name","metadata":null,"patterns":[]}],"metadata":null,"description":null},{"entity":"sys-number","values":[],"metadata":null,"description":null},{"entity":"music_genres","values":[{"type":"synonyms","value":"classical","metadata":null,"synonyms":[]},{"type":"synonyms","value":"hip hop","metadata":null,"synonyms":[]},{"type":"synonyms","value":"rock","metadata":null,"synonyms":[]},{"type":"synonyms","value":"pop","metadata":null,"synonyms":[]},{"type":"synonyms","value":"jazz","metadata":null,"synonyms":[]}],"metadata":null,"description":null},{"entity":"sys-time","values":[],"metadata":null,"description":null},{"entity":"sys-person","values":[],"metadata":null,"description":null},{"entity":"sys-location","values":[],"metadata":null,"description":null},{"entity":"device","values":[{"type":"synonyms","value":"all","metadata":null,"synonyms":[]},{"type":"synonyms","value":"thermostat","metadata":null,"synonyms":["ac","temperature","fan","heat","air"]},{"type":"synonyms","value":"tv","metadata":null,"synonyms":["television"]},{"type":"synonyms","value":"media_player","metadata":null,"synonyms":["roku","chromecast","firestick"]},{"type":"synonyms","value":"blinds","metadata":null,"synonyms":["shades"]},{"type":"synonyms","value":"projector screen","metadata":null,"synonyms":[]},{"type":"synonyms","value":"projector","metadata":null,"synonyms":[]},{"type":"synonyms","value":"light","metadata":null,"synonyms":["lamp"]},{"type":"synonyms","value":"monitor","metadata":null,"synonyms":["screen"]}],"metadata":null,"description":null},{"entity":"music_artists","values":[{"type":"patterns","value":"artist","metadata":null,"patterns":[]}],"metadata":null,"description":null},{"entity":"sys-date","values":[],"metadata":null,"description":null},{"entity":"sys-currency","values":[],"metadata":null,"description":null},{"entity":"location","values":[{"type":"synonyms","value":"nightstand","metadata":null,"synonyms":[]},{"type":"synonyms","value":"desk","metadata":null,"synonyms":[]},{"type":"synonyms","value":"kitchen","metadata":null,"synonyms":[]},{"type":"synonyms","value":"porch","metadata":null,"synonyms":[]},{"type":"synonyms","value":"bathroom","metadata":null,"synonyms":[]},{"type":"synonyms","value":"bedroom","metadata":null,"synonyms":[]},{"type":"synonyms","value":"living room","metadata":null,"synonyms":["family room"]}],"metadata":null,"description":null},{"entity":"clock","values":[{"type":"synonyms","value":"alarm","metadata":null,"synonyms":[]},{"type":"synonyms","value":"timer","metadata":null,"synonyms":[]}],"metadata":null,"description":null,"fuzzy_match":false}],"language":"en","metadata":{"api_version":{"major_version":"v1","minor_version":"2017-05-26"}},"description":null,"dialog_nodes":[{"type":"response_condition","title":null,"output":{"text":{"values":["setting an alarm for @sys-time"],"selection_policy":"sequential"}},"parent":"node_1_1509660647644","context":null,"metadata":null,"next_step":null,"conditions":"","description":null,"dialog_node":"node_2_1509660671771","previous_sibling":null},{"type":"standard","title":null,"output":{},"parent":null,"context":null,"metadata":{},"next_step":null,"conditions":"@sys-person && @music","description":null,"dialog_node":"node_1_1509668861134","previous_sibling":"node_1_1509660647644"},{"type":"standard","title":null,"output":null,"parent":null,"context":null,"metadata":null,"next_step":null,"conditions":"anything_else","description":null,"dialog_node":"node_2_1493939813350","previous_sibling":"node_1_1509668861134"},{"type":"standard","title":"timer","output":{},"parent":null,"context":null,"metadata":{"_customization":{"mcr":true}},"next_step":null,"conditions":"#timer && @sys-time","description":null,"dialog_node":"node_1_1509660647644","previous_sibling":null}],"workspace_id":"4d1dc2f9-c3a7-4fd6-90b8-cab7b073415e","counterexamples":[],"learning_opt_out":false}
--------------------------------------------------------------------------------
/ibm_cloud_services/node_red/node-red.json:
--------------------------------------------------------------------------------
1 | [{"type":"tab","id":"3a2571fb.e0242e","label":"Flow 1"},{"type":"tab","id":"e711dbd4.4e7d18","label":"Flow 2"},{"id":"675f49cf.4c3658","type":"mqtt-broker","z":"3a2571fb.e0242e","broker":"broker.hivemq.com","port":"1883","clientid":"","usetls":false,"verifyservercert":true,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willRetain":null,"willPayload":"","birthTopic":"node temp mqtt subscribe","birthQos":"1","birthRetain":null,"birthPayload":""},{"id":"70e30f1c.797ef","type":"mqtt-broker","z":"e711dbd4.4e7d18","broker":"agf5n9.messaging.internetofthings.ibmcloud.com","port":"1883","clientid":"d:agf5n9:MQTTDevice:965d11de","usetls":false,"verifyservercert":true,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willRetain":null,"willPayload":"","birthTopic":"","birthQos":"0","birthRetain":null,"birthPayload":""},{"id":"3ff15dfb.9c6542","type":"openwhisk","z":"e711dbd4.4e7d18","org":"kkbankol@us.ibm.com","space":"dev","action":"nlcjs","x":64.5,"y":181,"wires":[["4c32c17c.533de"]]},{"id":"4c32c17c.533de","type":"debug","z":"e711dbd4.4e7d18","name":"","active":true,"console":"false","complete":"payload","x":288.5,"y":181,"wires":[]}]
--------------------------------------------------------------------------------
/ibm_cloud_services/twilio/.env.example:
--------------------------------------------------------------------------------
1 | # Service credentials and classifier id
2 | NLC_USER=
3 | NLC_PASSWORD=
4 |
5 | NLC_CLASSIFIER_ID=
6 |
--------------------------------------------------------------------------------
/ibm_cloud_services/twilio/README.md:
--------------------------------------------------------------------------------
1 | ### Node.js app
2 |
3 |
--------------------------------------------------------------------------------
/ibm_cloud_services/twilio/app.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config()
2 | const express = require('express')
3 | const bodyParser = require('body-parser')
4 | const NaturalLanguageClassifierV1 = require('watson-developer-cloud/natural-language-classifier/v1');
5 |
6 | const natural_language_classifier = new NaturalLanguageClassifierV1({
7 | username: process.env.NLC_USER,
8 | password: process.env.NLC_PASSWORD
9 | });
10 |
11 | const app = express()
12 |
13 | app.use(bodyParser.urlencoded({ extended: false }))
14 | app.use(bodyParser.json());
15 |
16 | app.post('/sms', function(request, response) {
17 | const message = req.body.Body
18 | console.log(message)
19 | parse(message)
20 | .then(response => {
21 |
22 | })
23 | .catch(error => {
24 |
25 | });
26 | })
27 |
28 | app.post('/parse', function(request, response) {
29 | var message = request.body.text
30 | console.log(message)
31 | parse(message)
32 | .then(result => {
33 | response.json(result);
34 | })
35 | .catch(error => {
36 |
37 | });
38 | })
39 |
40 | function parse(message) {
41 | return new Promise(
42 | function(resolve, reject) {
43 | natural_language_classifier.classify({
44 | text: message,
45 | classifier_id: process.env.NLC_CLASSIFIER_ID
46 | },
47 | function(err, response) {
48 | if (err) {
49 | reject(err);
50 | } else {
51 | resolve(response.classes)
52 | }
53 | });
54 | });
55 | }
56 |
57 | app.listen(8000, function(err) {
58 | if (err) {
59 | throw err
60 | }
61 |
62 | console.log('Service started on port 8000')
63 | })
64 |
--------------------------------------------------------------------------------
/ibm_cloud_services/twilio/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "home-automation",
3 | "version": "1.0.0",
4 | "main": "app.js",
5 | "dependencies": {
6 | "body-parser": "^1.15.2",
7 | "dotenv": "^4.0.0",
8 | "express": "^4.14.0",
9 | "watson-developer-cloud": "^2.14.8"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/images/Architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/Architecture.jpg
--------------------------------------------------------------------------------
/images/Architecturev2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/Architecturev2.jpg
--------------------------------------------------------------------------------
/images/adddevice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/adddevice.png
--------------------------------------------------------------------------------
/images/adddevice_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/adddevice_menu.png
--------------------------------------------------------------------------------
/images/configure_messaging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/configure_messaging.png
--------------------------------------------------------------------------------
/images/configure_messaging_generic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/configure_messaging_generic.png
--------------------------------------------------------------------------------
/images/createdevicetype.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/createdevicetype.png
--------------------------------------------------------------------------------
/images/devicecreds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/devicecreds.png
--------------------------------------------------------------------------------
/images/generateapikey_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/generateapikey_menu.png
--------------------------------------------------------------------------------
/images/gpio_output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/gpio_output.png
--------------------------------------------------------------------------------
/images/hardware_arch.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/hardware_arch.jpeg
--------------------------------------------------------------------------------
/images/home_automation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/home_automation.png
--------------------------------------------------------------------------------
/images/home_automation_bb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/home_automation_bb.jpg
--------------------------------------------------------------------------------
/images/home_automation_bb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/home_automation_bb.png
--------------------------------------------------------------------------------
/images/home_automation_labeled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/home_automation_labeled.png
--------------------------------------------------------------------------------
/images/home_automation_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/home_automation_screen.png
--------------------------------------------------------------------------------
/images/iotwelcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/iotwelcome.png
--------------------------------------------------------------------------------
/images/noderedscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/noderedscreen.png
--------------------------------------------------------------------------------
/images/serverless_flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/serverless_flow.png
--------------------------------------------------------------------------------
/images/service_create.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/service_create.png
--------------------------------------------------------------------------------
/images/service_find.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/service_find.png
--------------------------------------------------------------------------------
/images/shortened_fritzing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/shortened_fritzing.jpg
--------------------------------------------------------------------------------
/images/stt_creds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/stt_creds.png
--------------------------------------------------------------------------------
/images/twilio_phone_numbers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/twilio_phone_numbers.png
--------------------------------------------------------------------------------
/images/watsoniotimage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IBM/serverless-home-automation/15d42fe59f305e8f0652bf5e386b86508174c0bf/images/watsoniotimage.png
--------------------------------------------------------------------------------
/iot-gateway/README.md:
--------------------------------------------------------------------------------
1 | ### TODO
2 | This guide provides a step by step tutorial to equip users to control power outlets using a Raspberry PI. The voice/text commands to trigger the outlets will be processed by IBM Watson's natural language services.
3 |
4 | ## Create credentials for Watson IoT Platform (MQTT Broker)
5 | Login to [IBM Cloud](https://console.ng.bluemix.net)
6 |
7 | Select the service [Catalog](https://console.ng.bluemix.net/catalog/) icon in the upper right
8 |
9 | Select and create the "Watson IoT Platform" service
10 |
11 |
12 |
13 |
14 |
15 | Once the Watson IoT Platform has been successfully created, click the "Launch" button to access the Dashboard
16 |
17 |
18 |
19 |
20 |
21 | Register your Raspberry PI with the Watson IoT Platform. This can be done by navigating to the Devices tab in the dashboard, and selecting "Add Device"
22 |
23 |
24 |
25 |
26 |
27 | Select "Create a device type"
28 |
29 |
30 |
31 |
32 |
33 | Enter a name for the device type. We used "homeAutomation". Continue pressing "Next" to create the device type and to Add a device. Enter a unique string of numbers and letters as the Device ID.
34 |
35 |
36 |
37 |
38 |
39 | Next, we'll need to generate an API key to serve as credentials for the MQTT broker. Select the "Apps" option in the sidebar, and click "Generate API Key".
40 |
41 |
42 |
43 |
44 |
45 |
46 | Take note of the API Key and Authentication Token, as they will be required to access the secure MQTT broker from IBM Cloud Functions and the Raspberry Pi. In the cloned repository on the Raspberry Pi, place the credentials in the [node_mqtt.js](node_mqtt.js) file
47 |
48 |
49 |
50 |
51 |
52 |
69 |
70 |
71 |
111 |
--------------------------------------------------------------------------------
/iot-gateway/devices.json:
--------------------------------------------------------------------------------
1 | {
2 | "devices": [
3 | {
4 | "location": "kitchen",
5 | "class": "light",
6 | "controlType": {
7 | "rf": {
8 | "config": {
9 | "on": "",
10 | "off": ""
11 | }
12 | }
13 | }
14 | },
15 | {
16 | "location": "bedroom",
17 | "class": "blinds",
18 | "controlType": {
19 | "rf": {
20 | "config": {
21 | "lower": "",
22 | "higher": ""
23 | }
24 | }
25 | }
26 | },
27 | {
28 | "location": "bedroom",
29 | "name": "samsung_bedroom",
30 | "class": "television",
31 | "CEC": true,
32 | "states": {
33 | "power": "off",
34 | "channel": "302"
35 | },
36 | "controlType": {
37 | "ir": {
38 | "config": {
39 | "lirc": "samsung_bedroom.conf"
40 | }
41 | }
42 | }
43 | },
44 | {
45 | "location": "bedroom",
46 | "class": "media_player",
47 | "controlType": {
48 | "cast": {
49 | "name": "chromecast"
50 | }
51 | },
52 | "belongs_to": "samsung_bedroom"
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/iot-gateway/install_deps.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sudo apt-get -y update
4 | sudo apt-get -y install git-core build-essential python-setuptools python-pyaudio python3-pyaudio sox make pulseaudio alsa-utils flac
5 | sudo easy_install pip
6 |
7 | if [ -f ~/.asoundrc ] ; then
8 | echo "Backing up .asoundrc"
9 | cp ~/.asoundrc "~/.asoundrc.bak.$(date +%s)"
10 | fi
11 | cp ../audio_interface/.asoundrc ~/.asoundrc
12 |
13 | libs_dir="/opt/home_libs"
14 | sudo mkdir -p $libs_dir
15 | sudo chown pi:pi $libs_dir
16 |
17 |
18 | ## Swig
19 | #sudo apt-get -y install swig3.0 # apt-get version outdated, have to build from source
20 | #TODO, add validated swig url or use git
21 | # git clone https://github.com/swig/swig
22 | sudo apt-get -y install libpcre3 libpcre3-dev autoconf python-dev byacc flex
23 | cd $libs_dir
24 | wget http://ufpr.dl.sourceforge.net/project/swig/swig/swig-3.0.12/swig-3.0.12.tar.gz
25 | tar -xzf swig-3.0.12.tar.gz
26 | cd swig-3.0.12/
27 | ./configure
28 | make
29 | sudo make install
30 |
31 | ## Snowboy Wakeword
32 | cd $libs_dir
33 | sudo apt-get -y install libatlas-base-dev
34 | sudo pip install pyaudio
35 | git clone https://github.com/kkbankol-ibm/snowboy/
36 | #git clone https://github.com/Kitt-AI/snowboy/
37 | cd snowboy/swig/Python
38 | make
39 |
40 | ## wiringPi
41 | cd $libs_dir
42 | git clone git://git.drogon.net/wiringPi
43 | cd wiringPi
44 | git pull origin master
45 | ./build
46 |
47 | ## 433Utils
48 | cd $libs_dir
49 | git clone --recursive git://github.com/ninjablocks/433Utils.git
50 | cd 433Utils/RPi_utils
51 | make
52 |
53 | # Install node / npm if needed
54 | if [[ ! "$(command -v npm)" || ! "$(command -v node)" ]] ; then
55 | echo "Node / npm is not installed, running install script"
56 | curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -
57 | sudo apt-get install -y build-essential
58 | sudo apt-get install -y nodejs
59 | fi
60 | npm install mqtt
61 |
--------------------------------------------------------------------------------
/iot-gateway/node-mqtt.js:
--------------------------------------------------------------------------------
1 | /*
2 | For this script to work, the following env variables will need to be defined beforehand
3 |
4 | Watson IoT Credentials, which can be found in the dashboard "Devices" and "Apps" sections
5 | - IOT_API_KEY
6 | - IOT_AUTH_TOKEN
7 | - IOT_ORG
8 | - IOT_DEVICE_TYPE
9 |
10 | On/Off RF codes for each devices we'd like to control, in the following format
11 | - RF_FAN_OFF
12 | - RF_FAN_ON
13 | */
14 |
15 | module.paths.push('/usr/lib/node_modules')
16 | var mqtt = require('mqtt');
17 | var exec = require('child_process').exec;
18 | // var _ = require('underscore')
19 | var mqttBroker = 'mqtts://' + process.env.IOT_ORG + '.messaging.internetofthings.ibmcloud.com'
20 | var mqttOptions = {
21 | username: process.env.IOT_API_KEY,
22 | password: process.env.IOT_AUTH_TOKEN,
23 | clientId: 'a:' + process.env.IOT_ORG + ':server1'
24 | }
25 | var mqttChannel = 'iot-2/type/' + process.env.IOT_DEVICE_TYPE + '/id/' + process.env.IOT_DEVICE_ID + '/evt/query/fmt/json'
26 |
27 | var mqttClient = mqtt.connect(mqttBroker, mqttOptions);
28 | mqttClient.on('connect', function () {
29 | mqttClient.subscribe(mqttChannel);
30 | console.log("connected");
31 | });
32 |
33 | mqttClient.on('subscribe', function () {
34 | console.log("subscribed to " + mqttChannel);
35 | });
36 | function getEntity(entities, entity){
37 | for(var ent in entities) {
38 | if (entities[ent].value == entity) {
39 | return true;
40 | }
41 | }
42 | return false
43 | }
44 | function getIntent(intents, intent){
45 | for (var intnt in intents) {
46 | if (intents[intnt].intent == undefined)
47 | continue
48 | if (intents[intnt].intent == intent) {
49 | return true;
50 | }
51 | }
52 | return false
53 | }
54 |
55 | mqttClient.on('message', function (topic, message) {
56 | console.log(message.toString('utf8'))
57 | var result = JSON.parse(message.toString('utf8')).d
58 | if (getIntent(result.intents, "turnon") && getEntity(result.entities, "light")){
59 | console.log("Turning on light")
60 | exec("/opt/433Utils/RPi_utils/codesend " + process.env.RF_LIGHT_ON + " -l 174 -p 0", function (error, stdout, stderr) {
61 | console.log(' ' + stdout);
62 | console.log('stderr: ' + stderr);
63 | });
64 | } else if ( getIntent(result.intents, "turnoff") && getEntity(result.entities, "light")) {
65 | console.log("Turning off light")
66 | exec("/opt/433Utils/RPi_utils/codesend " + process.env.RF_LIGHT_OFF +" -l 172 -p 0", function (error, stdout, stderr) {
67 | console.log(' ' + stdout);
68 | console.log('stderr: ' + stderr);
69 | });
70 | } else if ( getIntent(result.intents, "turnon") && getEntity(result.entities, "fan")) {
71 | console.log("Turning on fan")
72 | exec("/opt/433Utils/RPi_utils/codesend "+process.env.RF_FAN_ON +" -l 190 -p 0", function (error, stdout, stderr) {
73 | console.log(' ' + stdout);
74 | console.log('stderr: ' + stderr);
75 | });
76 | } else if ( getIntent(result.intents, "turnoff") && getEntity(result.entities, "fan")) {
77 | console.log("Turning off fan")
78 | exec("/opt/433Utils/RPi_utils/codesend " + process.env.RF_FAN_OFF + " -l 190 -p 0", function (error, stdout, stderr) {
79 | console.log(' ' + stdout);
80 | console.log('stderr: ' + stderr);
81 | });
82 | } else if ( getIntent(result.intents, "turnon") && getEntity(result.entities, "radio")) {
83 | console.log("Turning on clock")
84 | exec("/opt/433Utils/RPi_utils/codesend "+ process.env.RF_FAN_ON + " -l 190 -p 0", function (error, stdout, stderr) {
85 | console.log(' ' + stdout);
86 | console.log('stderr: ' + stderr);
87 | });
88 | } else if ( getIntent(result.intents, "turnoff") && getEntity(result.entities, "radio")) {
89 | console.log("Turning off clock")
90 | exec("/opt/433Utils/RPi_utils/codesend " + process.env.RF_FAN_OFF + " -l 190 -p 0", function (error, stdout, stderr) {
91 | console.log(' ' + stdout);
92 | console.log('stderr: ' + stderr);
93 | });
94 | } else {
95 | console.log("Message received but no matches found")
96 | }
97 | })
98 |
--------------------------------------------------------------------------------
/iot-gateway/node-mqtt.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Node MQTT Client
3 |
4 | [Service]
5 | Restart=always
6 | ExecStart=/usr/bin/node /opt/serverless-home-automation/iot-gateway/node-mqtt.js
7 |
8 | [Install]
9 | WantedBy=multi-user.target
10 |
11 |
--------------------------------------------------------------------------------