initial upload
authorJeonghoon Park <jh1979.park@samsung.com>
Tue, 13 Nov 2018 08:02:02 +0000 (17:02 +0900)
committerJeonghoon Park <jh1979.park@samsung.com>
Thu, 15 Nov 2018 11:25:13 +0000 (20:25 +0900)
Change-Id: Ibc6425a9fd0bba44dce7338b3541c8754bf7df94

85 files changed:
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
NOTICE [new file with mode: 0644]
README.md [new file with mode: 0644]
dashboard/default.gif [new file with mode: 0644]
dashboard/public/css/style.css [new file with mode: 0644]
dashboard/public/image/arrow-01.png [new file with mode: 0644]
dashboard/public/image/arrow-02.png [new file with mode: 0644]
dashboard/public/image/arrow-03.png [new file with mode: 0644]
dashboard/public/image/arrow-04.png [new file with mode: 0644]
dashboard/public/image/arrow-05.png [new file with mode: 0644]
dashboard/public/image/arrow-06.png [new file with mode: 0644]
dashboard/public/image/arrow-07.png [new file with mode: 0644]
dashboard/public/image/arrow-08.png [new file with mode: 0644]
dashboard/public/image/bg.png [new file with mode: 0644]
dashboard/public/image/bullet.png [new file with mode: 0644]
dashboard/public/image/image.png [new file with mode: 0644]
dashboard/public/image/mobile.png [new file with mode: 0644]
dashboard/public/image/shadow.png [new file with mode: 0644]
dashboard/public/index.html [new file with mode: 0644]
dashboard/public/js/app.js [new file with mode: 0644]
dashboard/server.js [new file with mode: 0644]
include/controller.h [new file with mode: 0644]
include/controller_image.h [new file with mode: 0644]
include/controller_mv.h [new file with mode: 0644]
include/exif.h [new file with mode: 0644]
include/log.h [new file with mode: 0644]
include/motion.h [new file with mode: 0644]
include/resource_camera.h [new file with mode: 0644]
include/resource_servo_motor_sg90.h [new file with mode: 0644]
include/servo-h.h [new file with mode: 0644]
include/servo-type.h [new file with mode: 0644]
include/servo-v.h [new file with mode: 0644]
include/smartthings.h [new file with mode: 0644]
include/smartthings_notification.h [new file with mode: 0644]
include/smartthings_payload.h [new file with mode: 0644]
include/smartthings_resource.h [new file with mode: 0644]
include/st_thing_master.h [new file with mode: 0644]
include/st_thing_resource.h [new file with mode: 0644]
include/switch.h [new file with mode: 0644]
install-and-start.sh [new file with mode: 0755]
iot-vision-camera.manifest [new file with mode: 0644]
lib/libst_thing_master_api.so [new file with mode: 0644]
lib/libst_thing_resource_api.so [new file with mode: 0644]
packaging/iot-dashboard.service [new file with mode: 0644]
packaging/iot-vision-camera.spec [new file with mode: 0644]
shared/res/default_icon.png [new file with mode: 0644]
shared/res/master.json [new file with mode: 0644]
shared/res/resource.json [new file with mode: 0644]
smartthings-plugin/device-profile.json [new file with mode: 0644]
smartthings-plugin/manifest/ui.json [new file with mode: 0644]
smartthings-plugin/plugin/css/bulma-switch.min.css [new file with mode: 0644]
smartthings-plugin/plugin/css/bulma.min.css [new file with mode: 0644]
smartthings-plugin/plugin/css/helper.css [new file with mode: 0644]
smartthings-plugin/plugin/icon.png [new file with mode: 0644]
smartthings-plugin/plugin/index.html [new file with mode: 0644]
smartthings-plugin/plugin/js/capability_modeSwitch.js [new file with mode: 0644]
smartthings-plugin/plugin/js/capability_motionSensor.js [new file with mode: 0644]
smartthings-plugin/plugin/js/capability_servoMotor.js [new file with mode: 0644]
smartthings-plugin/plugin/js/index.js [new file with mode: 0644]
smartthings-plugin/plugin/manifest.xml [new file with mode: 0644]
smartthings-plugin/plugin/res/arrow_down.png [new file with mode: 0644]
smartthings-plugin/plugin/res/arrow_left.png [new file with mode: 0644]
smartthings-plugin/plugin/res/arrow_right.png [new file with mode: 0644]
smartthings-plugin/plugin/res/arrow_up.png [new file with mode: 0644]
smartthings-plugin/plugin/res/board_ic_arrow_left.png [new file with mode: 0644]
smartthings-plugin/plugin/res/controller_bg.png [new file with mode: 0644]
smartthings-plugin/plugin/webfonts/fa-solid-900.ttf [new file with mode: 0644]
smartthings-plugin/plugin/webfonts/fa-solid-900.woff [new file with mode: 0644]
smartthings-plugin/plugin/webfonts/fa-solid-900.woff2 [new file with mode: 0644]
smartthings-plugin/prod-catalog.json [new file with mode: 0644]
src/controller.c [new file with mode: 0644]
src/controller_image.c [new file with mode: 0644]
src/controller_mv.c [new file with mode: 0644]
src/exif.c [new file with mode: 0644]
src/motion.c [new file with mode: 0644]
src/resource_camera.c [new file with mode: 0644]
src/resource_servo_motor_sg90.c [new file with mode: 0644]
src/servo-h.c [new file with mode: 0644]
src/servo-v.c [new file with mode: 0644]
src/st_thing_master.c [new file with mode: 0644]
src/st_thing_resource.c [new file with mode: 0644]
src/switch.c [new file with mode: 0644]
tizen-manifest.xml.in [new file with mode: 0644]
update-dashboard.sh [new file with mode: 0755]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..aaab0c4
--- /dev/null
@@ -0,0 +1,57 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+SET(CMAKE_VERBOSE_MAKEFILE 0)
+
+PROJECT(${P_NAME} C)
+SET(INSTALL_EXEC_PREFIX "${INSTALL_PREFIX}/bin")
+SET(PROJECT_ROOT_DIR "${CMAKE_SOURCE_DIR}")
+SET(PROJECT_RESOURCES_DIR "${PROJECT_ROOT_DIR}/res")
+SET(CMAKE_INSTALL_RPATH "${INSTALL_PREFIX}/lib")
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(APP_PKGS REQUIRED
+       dlog
+       capi-appfw-service-application
+       capi-system-peripheral-io
+       capi-media-camera
+       capi-media-vision
+       capi-media-image-util
+       libtzplatform-config
+       glib-2.0
+       gio-2.0
+       ecore
+       rpc-port
+       libexif
+       capi-appfw-package-manager
+)
+
+FOREACH (flag ${APP_PKGS_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Wall -Winline -g -fno-builtin-malloc -fPIE")
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie")
+
+LINK_DIRECTORIES(${PROJECT_ROOT_DIR}/lib)
+SET(ST_API_LIBS st_thing_master_api st_thing_resource_api)
+
+INCLUDE_DIRECTORIES(${PROJECT_ROOT_DIR}/include)
+
+FILE(GLOB APP_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${APP_SOURCE})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${APP_PKGS_LDFLAGS} -lm ${ST_API_LIBS})
+
+CONFIGURE_FILE(${PROJECT_ROOT_DIR}/tizen-manifest.xml.in ${PROJECT_NAME}.xml @ONLY)
+
+# Install
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${INSTALL_EXEC_PREFIX})
+INSTALL(FILES ${PROJECT_NAME}.xml DESTINATION ${SYS_PACKAGES_DIR})
+INSTALL(DIRECTORY ${PROJECT_ROOT_DIR}/lib DESTINATION ${INSTALL_PREFIX})
+INSTALL(FILES ${PROJECT_ROOT_DIR}/shared/res/default_icon.png DESTINATION ${APP_SHARED_RES_DIR} RENAME ${PROJECT_NAME}.png)
+INSTALL(DIRECTORY ${PROJECT_ROOT_DIR}/res DESTINATION ${INSTALL_PREFIX})
+INSTALL(FILES ${PROJECT_ROOT_DIR}/shared/res/master.json DESTINATION ${APP_SHARED_RES_DIR})
+INSTALL(FILES ${PROJECT_ROOT_DIR}/shared/res/resource.json DESTINATION ${APP_SHARED_RES_DIR})
+INSTALL(DIRECTORY ${PROJECT_ROOT_DIR}/dashboard DESTINATION ${DASH_BOARD_DIR})
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..90ce5ad
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,203 @@
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2018 Samsung Electronics Co., Ltd.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/NOTICE b/NOTICE
new file mode 100644 (file)
index 0000000..26a0552
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..68ff31a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,159 @@
+# IoT Vision App
+Analyze images captured by USB camera and then move camera angle by using servo motors.
+
+## HOW TO RUN - First run
+
+### 1. Flash binary
+Tizen 5.0 M2 IoT 부트 이미지 다운로드 [[링크](http://download.tizen.org/releases/milestone/tizen/unified/tizen-unified_20181024.1/images/standard/iot-boot-armv7l-artik533s/)]
+
+Tizen 5.0 M2 Iot Headed 이미지 다운로드 [[링크](http://download.tizen.org/releases/milestone/tizen/unified/tizen-unified_20181024.1/images/standard/iot-headed-3parts-armv7l-artik530_710/)]
+```
+sudo minicom
+    thordown
+lthor tizen-unified_20181024.1_iot-boot-armv7l-artik533s.tar.gz tizen-unified_20181024.1_iot-headed-3parts-armv7l-artik530_710.tar.gz
+```
+
+### 2. Install plug-in
+
+ARTIK 530(5.0) Plugin download from https://developer.samsung.com/tizendevice/firmware
+
+
+### 3. Install Wifi util (*optional)
+Tizen 5.0 M2 wifi-manager-tool 다운로드 [[링크](http://download.tizen.org/releases/milestone/tizen/unified/tizen-unified_20181024.1/repos/standard/packages/armv7l/capi-network-wifi-manager-tool-1.0.39-81.5.armv7l.rpm)]
+```
+sdb root on; sdb shell 'mount -o remount,rw /'
+
+sdb push capi-network-wifi-manager-tool-1.0.39-81.5.armv7l.rpm /tmp
+
+sdb shell 'rpm -ivh /tmp/capi-network-wifi-manager-tool-1.0.39-81.5.armv7l.rpm'
+```
+
+### 4. Build and install custom version of IoT.js to include WebSocket feature
+```
+git clone https://github.com/Samsung/iotjs.git
+
+cd iotjs
+
+sed -i 's/Release: 0/Release: 99/' config/tizen/packaging/iotjs.spec
+
+sed -i '/--no-parallel-build/a \ \ --cmake-param=-DENABLE_MODULE_WEBSOCKET=ON \\' config/tizen/packaging/iotjs.spec
+
+git diff config/tizen/packaging/iotjs.spec
+
+./config/tizen/gbsbuild.sh --clean
+
+sdb push ~/GBS-ROOT/local/repos/tizen_unified_m1/armv7l/RPMS/iotjs-1.0.0-9.0.armv7l.rpm /tmp
+
+sdb shell 'cd /tmp; rpm -ivh iotjs-1.0.0-9.0.armv7l.rpm'
+```
+- https://github.com/Samsung/iotjs/wiki/Build-for-RPi3-Tizen
+- https://github.com/Samsung/iotjs/blob/master/docs/api/IoT.js-API-WebSocket.md
+
+
+### 5. Change camera setting
+```
+sdb shell "cat /etc/multimedia/mmfw_camcorder_camera0.ini | grep 640"
+
+sdb shell "sed -i 's/640,480/320,240/g' /etc/multimedia/mmfw_camcorder_camera0.ini"
+
+sdb shell "cat /etc/multimedia/mmfw_camcorder_camera0.ini | grep 320"
+```
+
+### 6. Build and install vision app
+```
+git clone git://git.tizen.org/apps/native/smart-surveillance-camera # This git
+
+cd smart-surveillance-camera
+
+gbs build -A armv7l --include-all
+
+sdb push ~/GBS-ROOT/local/BUILD-ROOTS/scratch.armv7l.0/home/abuild/rpmbuild/RPMS/armv7l/iot-vision-camera-0.0.1-1.armv7l.rpm /tmp
+
+sdb shell 'rpm -ivh --force /tmp/iot-vision-camera-0.0.1-1.armv7l.rpm'
+```
+### 7. Some more settings
+```
+sdb shell 'pkg_initdb'
+```
+### 8. Launch vision app
+```
+sdb shell 'app_launcher -s iot-vision-camera'
+```
+### 9. Check vision app activity
+```
+sdb shell 'ls -l /tmp/latest.jpg'
+```
+### 10. Install and run monitor server
+* If you install latest version of "iot-vision-camera" package, the monitor server is automatically launched in booting time
+
+* To check status of the monitor server
+       ```
+       sdb shell 'systemctl status iot-dashboard'
+       ```
+
+* To restart the monitor server
+       ```
+       sdb shell 'systemctl stop iot-dashboard'
+       sdb shell 'systemctl start iot-dashboard'
+       sdb shell 'systemctl status iot-dashboard'  # To check status
+       ```
+
+* To update some files of the monitor server, push the files to dashboard path `/opt/home/dashboard` and restart the monitor server as above
+       ```
+       sdb shell push {path/yourfile} /opt/home/dashboard/{path}
+       ```
+OR! YOU CAN SIMPLY USE THE SCRIPT:
+```
+./update-dashboard.sh
+```
+
+
+## HOW TO RUN - Subsequent Runs
+```
+sdb root on; sdb shell 'mount -o remount,rw /'
+
+sdb shell 'app_launcher -s iot-vision-camera'
+
+ ./update-dashboard.sh
+```
+
+## Profiling Data
+
+### 카메라의 물리적 이동시간
+슈퍼슬로우 카메라로 촬영결과 500ms 정도 소요
+
+### Rpi
+
+#### Image Encoding (buffer -> jpg file)
+소요시간 20 ~ 135ms (가끔씩 오래걸림, 장담 못함)
+
+#### Vision Survailance (input -> cb event)
+소요시간 38 ~ 50ms
+
+
+### Artik
+
+#### Image Encoding (buffer -> jpg file)
+소요시간 10 ~ 20ms, 대부분 10ms 초반 안정적
+
+#### Vision Survailance (input -> cb event)
+소요시간 24 ~ 42ms
+
+## Vision 움직임 정보 형식 (exif)
+최대 244Byte 크기의 스트링으로 모두 숫자로 이루어져있다.
+
+첫 2자리 숫자는 분석결과의 타입으로 TT 의 값을 갖는다.
+
+그 다음 2자리 숫자는 포함된 움직임의 갯수 NN 의 값을 갖는다.
+
+하나의 움직임은 8개 숫자로 구성되며 xxyywwhh 의 값을 갖는다.
+
+xx: x 상대 좌표
+yy: y 상대 좌표
+ww: 상대 넓이
+hh: 상대 높이
+
+각각 0~99까지의 범위를 갖는 4개의 숫자를 이어놓은 것이다.
+1자리 숫자의 경우 0을 넣어서 전체 길이를 고정한다.
+
+TTNNxxyywwhhxxyywwhh....xxyywwhh 의 형태의 스트링이 된다.
diff --git a/dashboard/default.gif b/dashboard/default.gif
new file mode 100644 (file)
index 0000000..4818c1a
Binary files /dev/null and b/dashboard/default.gif differ
diff --git a/dashboard/public/css/style.css b/dashboard/public/css/style.css
new file mode 100644 (file)
index 0000000..8fdb5a2
--- /dev/null
@@ -0,0 +1,297 @@
+body {
+  width: 1920px;
+  height: 1080px;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+html {
+  width: 1920px;
+  height: 1080px;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+body {
+  margin: 0;
+  background: url("../image/bg.png") no-repeat fixed;
+  background-size: 1920px 1080px;
+}
+
+#text-container {
+  width: 1200px;
+  color: #ffffff;
+  padding-top: 50px;
+  margin-left: 110px;
+}
+
+#project-title {
+  font: 800 60px 'arial';
+  line-height: 70px;
+}
+
+#project-main-text {
+  font: 500 28px 'arial';
+  line-height: 48px;
+  margin-top: 20px;
+}
+
+ul {
+  list-style-image: url("../image/bullet.png");
+}
+
+li {
+  padding-left: 20px;
+}
+
+.nobull {
+  list-style: none;
+  list-style-type: none;
+  display: inline-block;
+}
+
+.try-it-bullet {
+  margin-left: -65px;
+  position: absolute;
+  font-size: 18px;
+  font-family: 'arial';
+  color: #FBB03B;
+  -ms-transform: rotate(-20deg); /* IE 9 */
+  -webkit-transform: rotate(-20deg); /* Safari */
+  transform: rotate(-20deg);
+}
+
+.shake-it {
+  animation: shake 2s cubic-bezier(.36,.07,.19,.97) both infinite;
+}
+
+@keyframes shake {
+  10%, 90%      { transform: rotate(-20deg) }
+  20%, 80%      { transform: rotate(20deg) }
+  30%, 50%, 70% { transform: rotate(-20deg) }
+  40%, 60%      { transform: rotate(20deg) }
+}
+
+.blink {
+  -webkit-animation: blink 2.0s linear infinite;
+}
+@-webkit-keyframes blink {
+  0% { opacity: 1.0; }
+  10% { opacity: 0.3; }
+  40% { opacity: 1.0; }
+  100% { opacity: 1.0; }
+/*    from { background-color: red;}
+  to {background-color: green;}  */
+}
+
+#diagram-container {
+  width: 1735px;
+  font: 800 36px 'arial';
+  margin: 60px 0 0 70px;
+}
+
+#diagram-bg {
+  position: absolute;
+  margin: 0 0 0 -20px;
+  width: 1020px;
+  height: 400px;
+  background-color: #ffffff;
+  opacity: 0.1;
+}
+
+#diagram-title {
+  font: 500 25px 'arial';
+  color: #FBB03B;
+  padding: 25px 0 0 15px;
+}
+
+.d-text-tizen {
+  color: #F47A0B;
+}
+
+.d-text-st {
+  color: #FBB03B;
+}
+
+.d-text-tizen {
+  text-shadow: 2px 2px 4px #113255;
+}
+
+.d-text-st {
+  text-shadow: 2px 2px 4px #113255;
+}
+
+.caption-text {
+  font: 500 18px 'arial';
+  line-height: 1px;
+  display: block;
+}
+
+.caption-arrow {
+  font: 500 18px 'arial';
+  color: #A9ABAC;
+  line-height: 1px;
+}
+
+.arrow-01 {
+  width: 51px;
+  background: url("../image/arrow-01.png") center no-repeat;
+}
+
+.arrow-02 {
+  width: 46px;
+  background: url("../image/arrow-02.png") no-repeat;
+  background-position: center bottom;
+  margin-bottom: -20px;
+}
+
+.arrow-03 {
+  width: 48px;
+  background: url("../image/arrow-03.png") no-repeat;
+  background-position: center top;
+  margin-top: -20px;
+}
+
+.arrow-04 {
+  width: 67px;
+  background: url("../image/arrow-04.png") center no-repeat;
+}
+
+.arrow-05 {
+  width: 305px;
+  background: url("../image/arrow-05.png") center no-repeat;
+}
+
+.arrow-06 {
+  width: 473px;
+  background: url("../image/arrow-06.png") center no-repeat;
+}
+
+.arrow-04, .arrow-05, .arrow-06 {
+  padding-top: 10px;
+}
+
+.caption-text {
+  margin-top:-10px;
+}
+
+.diagram-main > div {
+  display: inline-block;
+  height: 80px;
+  line-height: 80px;
+  text-align: center;
+  vertical-align: middle;
+  margin-right: 15px;
+}
+
+.two-line {
+  line-height: 40px !important;
+}
+
+#diagram-top {
+  margin: 15px 0 0 330px;
+}
+
+#diagram-middle {
+  margin: 10px 0 0 30px;
+}
+
+#diagram-bottom {
+  margin: 30px 0 0 335px;
+}
+
+#diagram-right {
+  position: absolute;
+  top: 490px;
+  left: 1420px;
+}
+
+#diagram-right > div {
+  position: absolute;
+}
+
+.arrow-07 {
+  width: 129px;
+  height: 361px;
+  background: url("../image/arrow-07.png") center no-repeat;
+  margin: 15px 0 0 -20px;
+}
+
+.arrow-08 {
+  width: 42px;
+  height: 85px;
+  background: url("../image/arrow-08.png") center no-repeat;
+  margin: 400px 0 0 280px;
+}
+
+#mobile {
+  width: 131px;
+  height: 261px;
+  background: url("../image/mobile.png") center no-repeat;
+  margin: 100px 0 0 250px;
+  text-align: center;
+  color: #ffffff;
+  font: 800 18px 'arial';
+}
+
+#mobile-detection {
+  margin-top: 100px;
+}
+
+.smaller-text {
+  font-size: 13px;
+  line-height: 10px;
+}
+
+#mobile-caption {
+  margin-top: 120px;
+}
+
+#camera-container {
+  position: absolute;
+  top: 5px;
+  left: 1100px;
+}
+
+#camera-container > div, img {
+  position: absolute;
+}
+
+#camera-view-bg {
+  z-index:-1;
+  width: 878px;
+  height: 658px;
+  background-image: url("../image/shadow.png");
+}
+
+#camera-view {
+  width: 640px;
+  height: 480px;
+  left: 90px;
+  top: 65px;
+  background-image: url("../image/image.png");
+  outline: 5px solid #FBB03B;
+}
+
+#camera-view-caption {
+  width: 878px;
+  top: 560px;
+  left: -15px;
+}
+
+.image-caption {
+  color: #8FC2F2;
+  font: 800 15px 'arial';
+  text-align: center;
+}
+
+#canvas-container canvas {
+  z-index: 10;
+  position: absolute;
+  left: 90px;
+  top: 65px;
+}
+
+#fps {
+  font-weight: 800;
+}
diff --git a/dashboard/public/image/arrow-01.png b/dashboard/public/image/arrow-01.png
new file mode 100644 (file)
index 0000000..dbc0e1c
Binary files /dev/null and b/dashboard/public/image/arrow-01.png differ
diff --git a/dashboard/public/image/arrow-02.png b/dashboard/public/image/arrow-02.png
new file mode 100644 (file)
index 0000000..9fa654f
Binary files /dev/null and b/dashboard/public/image/arrow-02.png differ
diff --git a/dashboard/public/image/arrow-03.png b/dashboard/public/image/arrow-03.png
new file mode 100644 (file)
index 0000000..1f1e85c
Binary files /dev/null and b/dashboard/public/image/arrow-03.png differ
diff --git a/dashboard/public/image/arrow-04.png b/dashboard/public/image/arrow-04.png
new file mode 100644 (file)
index 0000000..3304a30
Binary files /dev/null and b/dashboard/public/image/arrow-04.png differ
diff --git a/dashboard/public/image/arrow-05.png b/dashboard/public/image/arrow-05.png
new file mode 100644 (file)
index 0000000..061d6f1
Binary files /dev/null and b/dashboard/public/image/arrow-05.png differ
diff --git a/dashboard/public/image/arrow-06.png b/dashboard/public/image/arrow-06.png
new file mode 100644 (file)
index 0000000..d0afbbf
Binary files /dev/null and b/dashboard/public/image/arrow-06.png differ
diff --git a/dashboard/public/image/arrow-07.png b/dashboard/public/image/arrow-07.png
new file mode 100644 (file)
index 0000000..0a6b8f5
Binary files /dev/null and b/dashboard/public/image/arrow-07.png differ
diff --git a/dashboard/public/image/arrow-08.png b/dashboard/public/image/arrow-08.png
new file mode 100644 (file)
index 0000000..f5a9923
Binary files /dev/null and b/dashboard/public/image/arrow-08.png differ
diff --git a/dashboard/public/image/bg.png b/dashboard/public/image/bg.png
new file mode 100644 (file)
index 0000000..ca8c14f
Binary files /dev/null and b/dashboard/public/image/bg.png differ
diff --git a/dashboard/public/image/bullet.png b/dashboard/public/image/bullet.png
new file mode 100644 (file)
index 0000000..0b8edd5
Binary files /dev/null and b/dashboard/public/image/bullet.png differ
diff --git a/dashboard/public/image/image.png b/dashboard/public/image/image.png
new file mode 100644 (file)
index 0000000..7a7e3dc
Binary files /dev/null and b/dashboard/public/image/image.png differ
diff --git a/dashboard/public/image/mobile.png b/dashboard/public/image/mobile.png
new file mode 100644 (file)
index 0000000..c3d0ce9
Binary files /dev/null and b/dashboard/public/image/mobile.png differ
diff --git a/dashboard/public/image/shadow.png b/dashboard/public/image/shadow.png
new file mode 100644 (file)
index 0000000..8a93a4b
Binary files /dev/null and b/dashboard/public/image/shadow.png differ
diff --git a/dashboard/public/index.html b/dashboard/public/index.html
new file mode 100644 (file)
index 0000000..f696fe5
--- /dev/null
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta name="description" content="TEST PAGE">
+    <meta name="author" content="salt">
+
+    <title>Tizen IoT</title>
+
+    <!-- 합쳐지고 최소화된 최신 CSS -->
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
+    <link href="css/style.css" rel="stylesheet">
+</head>
+
+<body>
+    <div id="main-container">
+        <div id="text-container">
+            <div id="project-title">Tizen IoT<br>Smart Surveillance Camera</div>
+            <div id="project-main-text">
+                <ul>
+                    <li class="step1">Captures images continuously with a USB camera</li>
+                    <li class="step2">Detects motion of objects in the images</li>
+                    <li class="step3">Controls servomotors to follow the motion</li>
+                    <li class="step4">Sends the images to connected web clients</li>
+                    <li class="step5">Displays a live view of images in each web client</li>
+                    <li class="step6">Sends motion info to SmartThings Cloud</li>
+                    <li class="step7">Displays motion info on SmartThings Mobile App</li>
+                    <li class="step8 nobull"><span class="try-it-bullet">TRY IT!</span>You can manually control servomotors using the SmartThings Mobile App</li>
+                </ul>
+            </div>
+        </div>
+        <div id="diagram-container">
+            <div id="diagram-bg"></div>
+            <div id="diagram-title">Tizen IoT made in Craftroom</div>
+            <div id="diagram-top" class="diagram-main">
+                <div class="arrow-02 arrow step2"></div>
+                <div class="d-text-tizen step3 step8">Peripheral IO</div>
+                <div class="arrow-04 arrow step3 step8"><span class="caption-arrow">moving</span></div>
+                <div class="d-text-tizen step3 step8">Servomotor</div>
+            </div>
+            <div id="diagram-middle" class="diagram-main">
+                <div class="d-text-tizen step1" id="d-text-camera">Camera<span class="caption-text">capturing</span></div>
+                <div class="arrow-01 arrow step1"></div>
+                <div class="d-text-tizen step2" id="d-text-vision">Vision<span class="caption-text">analysing</span></div>
+                <div class="arrow-01 arrow step2"></div>
+                <div class="d-text-tizen step4">IoT.js</div>
+                <div class="arrow-06 arrow step4"><span class="caption-arrow">live streaming (<span id="fps">0</span> fps)</span></div>
+                <div class="d-text-st step5">Web Client<span class="caption-text">(this screen)</span></div>
+            </div>
+            <div id="diagram-bottom" class="diagram-main">
+                <div class="arrow-03 arrow step2"></div>
+                <div class="d-text-tizen step6 step8">SmartThings SDK</div>
+                <div class="arrow-05 arrow step6 step8"><span class="caption-arrow">transferring</span></div>
+                <div class="d-text-st two-line step6 step8">SmartThings<br>Cloud</div>
+                <div class="arrow-04 arrow step6 step8"></div>
+                <div class="d-text-st two-line step7 step8">SmartThings<br>Mobile App</div>
+            </div>
+            <div id="diagram-right">
+                <div class="arrow-07 arrow step5"></div>
+                <div class="arrow-08 arrow step7 step8"></div>
+                <div id="mobile">
+                    <div id="mobile-detection">Motion<br>Detected</div>
+                    <div id="mobile-caption" class="image-caption">Mobile Device</div>
+                </div>
+            </div>
+        </div>
+        <div id="camera-container">
+            <div id="canvas-container">
+                <canvas id="camera-view-canvas" width="640" height="480"></canvas>
+            </div>
+            <div id="camera-view-bg"></div>
+            <img id="camera-view"/>
+            <div id="camera-view-caption" class="image-caption">Camera Live View</div>
+        </div>
+    </div>
+    <!-- jQuery -->
+    <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
+    crossorigin="anonymous"></script>
+    <!-- 합쳐지고 최소화된 최신 자바스크립트 -->
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
+
+    <script src="js/app.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/exif-js"></script>
+</body>
+
+</html>
diff --git a/dashboard/public/js/app.js b/dashboard/public/js/app.js
new file mode 100644 (file)
index 0000000..554d58d
--- /dev/null
@@ -0,0 +1,266 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const IMG_WIDTH = 640;
+const IMG_HEIGHT = 480;
+const CANVAS_WIDTH = 640;
+const CANVAS_HEIGHT = 480;
+
+window.onload = function(){
+    var canvas;
+    var frame_timestamp = new Array(100);
+    var request_time;
+    var frame_number = 0;
+
+    var xhr = new XMLHttpRequest();
+    xhr.onreadystatechange = function() {
+        var previewElement = document.getElementById('camera-view');
+
+        if (xhr.readyState === 4) {
+            update_fps();
+            // console.log(JSON.stringify(frame_timestamp));
+
+            var latencyTag = document.getElementById('latency');
+            if (latencyTag != null)
+                latencyTag.innerHTML = 'latency: ' + (Date.now() - request_time) + ' ms';
+
+            previewElement.src = xhr.response;
+        }
+    };
+
+    canvas = new Canvas("camera-view-canvas");
+
+    runWebSocket();
+
+    function update_fps() {
+        frame_timestamp[frame_number] = Date.now();
+        var fpsTag = document.getElementById('fps');
+        if (fpsTag != null)
+            fpsTag.innerHTML = JSON.stringify(fps());
+        if (++frame_number == frame_timestamp.length) frame_number = 0;
+    }
+
+    function fps() {
+        var i = frame_number;
+        var now = Date.now();
+        do { // backtrace to find the frame 1000 milliseconds before
+            if (now - frame_timestamp[i] >= 1000) {
+                var fps = frame_number - i;
+                if (fps < 0)
+                    fps += frame_timestamp.length;
+                return fps;
+            }
+            if (--i < 0) // wrap around to the last slot in the array
+                i = frame_timestamp.length - 1;
+        } while (i != frame_number)
+    }
+
+    function runWebSocket() {
+        // var wsUri = "ws://192.168.1.211:8888/";
+        var wsUri = "ws://" + window.location.hostname + ":8888/";
+
+        websocket = new WebSocket(wsUri);
+        websocket.onopen = function(evt) { onOpen(evt) };
+        websocket.onclose = function(evt) { onClose(evt) };
+        websocket.onmessage = function(evt) { onMessage(evt) };
+        websocket.onerror = function(evt) { onError(evt) };
+
+        // // polling
+        // setInterval(function() {
+        //   // console.log('hey!');
+        //   xhr.open('POST', '/requestImage' , true);
+        //   xhr.send();
+        //   request_time = Date.now();
+        //   // console.log('update image');
+        // }, 100);
+    }
+
+    function onOpen(evt)
+    {
+        writeToScreen("CONNECTED");
+        doSend("HELLO FROM BROWSER via WebSocket!!!!!!!!!!!!");
+    }
+
+    function onClose(evt)
+    {
+        writeToScreen("DISCONNECTED");
+    }
+
+    function onMessage(evt)
+    {
+        // writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
+
+        var urlCreator = window.URL || window.webkitURL;
+        var imageUrl = urlCreator.createObjectURL(evt.data);
+        document.querySelector("#camera-view").src = imageUrl;
+        update_fps();
+
+        var arrayBuffer;
+        var fileReader = new FileReader();
+        fileReader.onload = function(event) {
+            arrayBuffer = event.target.result;
+            var exif = EXIF.readFromBinaryFile(arrayBuffer);
+            var exifInfoString = asciiToStr(exif.UserComment, 8);
+            var type = 'blur';
+            if (getResultType(exifInfoString) != 0) {
+                type = 'active';
+            }
+
+            var pointArray = getPointArrayFromString(exifInfoString.slice(4));
+
+            if (pointArray.length <= 0) {
+                document.querySelector("#mobile-detection").innerHTML = "No<br>Motion";
+            } else {
+                document.querySelector("#mobile-detection").innerHTML = "Motion<br>Detected";
+            }
+
+            canvas.clearPoints();
+            canvas.drawPoints(pointArray, type);
+        };
+        fileReader.readAsArrayBuffer(evt.data);
+
+        doSend("ack", true);
+
+        // websocket.close();
+
+        // var reader = new FileReader();
+        // reader.readAsDataURL(evt.data);
+        // reader.onloadend = function() {
+        //     base64data = reader.result;
+        //     var previewElement = document.getElementById('camera-view');
+        //     previewElement.src = base64data;
+        //     update_fps();
+        // }
+    }
+
+    function onError(evt)
+    {
+        writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
+    }
+
+    function doSend(message, dont_print_log)
+    {
+        websocket.send(message);
+        if (!dont_print_log)
+            writeToScreen("SENT: " + message);
+    }
+
+    function writeToScreen(message)
+    {
+        var output = document.getElementById("output");
+        if (output == null)
+            return;
+        var pre = document.createElement("p");
+        pre.style.wordWrap = "break-word";
+        pre.innerHTML = message;
+        output.appendChild(pre);
+    }
+
+    function asciiToStr(asciiArr, start) {
+        var string = "";
+        var i = start;
+
+        for (; i < asciiArr.length; i++) {
+            string += String.fromCharCode(asciiArr[i]);
+        }
+
+        return string;
+    }
+
+    function getResultType(str) {
+        return Number(str.slice(0, 2));
+    }
+
+    function getPointArrayFromString(pointStr) {
+        var strLen = pointStr.length;
+        var i = 0;
+        var pointArray = [];
+
+        while (i < strLen) {
+            pointArray.push(pointStr.slice(i, i + 8));
+            i += 8;
+        }
+
+        return pointArray;
+    }
+
+    var step = 0
+    const total_steps = 8
+    setInterval(function() {
+        for (var i = 0; i < total_steps; i++) {
+            var className = '.step' + (i + 1);
+            $('li' + className).removeClass('blink');
+            $('.d-text-tizen' + className).removeClass('blink');
+            $('.d-text-st' + className).removeClass('blink');
+            $('.arrow' + className).removeClass('blink');
+            $('.try-it-bullet').removeClass('shake-it');
+        }
+        var className = '.step' + (step + 1);
+        $('li' + className).addClass('blink');
+        $('.d-text-tizen' + className).addClass('blink');
+        $('.d-text-st' + className).addClass('blink');
+        $('.arrow' + className).addClass('blink');
+        if (step == 7)
+            $('.try-it-bullet').addClass('shake-it');
+        step = ++step % total_steps;
+    }, 4000);
+};
+
+function Canvas(canvasId) {
+    this.viewCanvas = document.getElementById(canvasId);
+    this.viewContext = this.viewCanvas.getContext("2d");
+}
+
+Canvas.prototype.clearCanvas = function() {
+    this.viewContext.clearRect(0,0, CANVAS_WIDTH, CANVAS_HEIGHT);
+}
+
+Canvas.prototype.clearPoints = function () {
+    this.clearCanvas();
+}
+
+Canvas.prototype.drawRect = function(x, y, width, height, color) {
+    this.viewContext.beginPath();
+    this.viewContext.moveTo(x, y);
+    this.viewContext.lineTo(x, y + height);
+    this.viewContext.lineTo(x + width, y + height);
+    this.viewContext.lineTo(x + width, y);
+    this.viewContext.closePath();
+    this.viewContext.lineWidth = 3;
+    this.viewContext.strokeStyle = color;
+    this.viewContext.stroke();
+}
+
+Canvas.prototype.drawPoints = function (pointArray, type) {
+    var i = 0;
+    var x, y, w, h;
+    var color;
+
+    if (type == 'blur') {
+        color = "rgba(115,232,57,0.8)";
+    } else {
+        color = "rgba(255,0,0, 0.8)";
+    }
+
+    for (i = 0; i < pointArray.length; i++) {
+        x = IMG_WIDTH / 99 * parseInt(pointArray[i].slice(0,2));
+        y = IMG_HEIGHT / 99 * parseInt(pointArray[i].slice(2,4));
+        w = IMG_WIDTH / 99 * parseInt(pointArray[i].slice(4,6));
+        h = IMG_HEIGHT / 99 * parseInt(pointArray[i].slice(6,8));
+
+        this.drawRect(x, y, w, h, color);
+    }
+}
diff --git a/dashboard/server.js b/dashboard/server.js
new file mode 100644 (file)
index 0000000..a5720f1
--- /dev/null
@@ -0,0 +1,124 @@
+ /*\r
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an AS IS BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+var fs = require('fs');\r
+var http = require('http');\r
+\r
+function extractPath(url) {\r
+  var urlParts = url.split('/'),\r
+    i = 0,\r
+    l = urlParts.length,\r
+    result = [];\r
+  for (; i < l; ++i) {\r
+    if (urlParts[i].length > 0) {\r
+      result.push(urlParts[i]);\r
+    }\r
+  }\r
+  return result;\r
+}\r
+\r
+http.createServer(function(req, res) {\r
+  req.on('end', function() {\r
+    var path = extractPath(req.url);\r
+    console.log(req.url)\r
+    // var last = path[path.length - 1];\r
+    if (path[0] === undefined) {\r
+      res.writeHead(200);\r
+      res.end(fs.readFileSync('public/index.html'));\r
+    } else if (path[0] == 'test') {\r
+      res.writeHead(200);\r
+      res.end(fs.readFileSync('public/test.html'));\r
+    } else if (req.url == '/js/app.js') {\r
+      res.writeHead(200);\r
+      res.end(fs.readFileSync('public/js/app.js'));\r
+    } else if (req.url == '/css/style.css') {\r
+      res.writeHead(200);\r
+      res.end(fs.readFileSync('public/css/style.css'));\r
+    } else {\r
+      res.setHeader('Location', 'http://download.tizen.online/smart-surveillance' + req.url);\r
+      res.writeHead(302);\r
+      res.end();\r
+      console.log('33333');\r
+    }\r
+  });\r
+}).listen(9090);\r
+\r
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
+\r
+var ENABLE_WEBSOCKET = true;\r
+\r
+if (ENABLE_WEBSOCKET) {\r
+\r
+  var websocket = require('websocket');\r
+\r
+  var options = {\r
+    port: 8888\r
+  }\r
+\r
+  var server = new websocket.Server(options, Listener);\r
+\r
+  function Listener(ws) {\r
+    console.log('Client connected: handshake done!');\r
+    ws.ack = true;\r
+    ws.on('message', function (msg) {\r
+      console.log('Message received: %s', msg.toString());\r
+      // ws.send(msg.toString(), {mask: true, binary: false}); //echo\r
+      // ws.send('Received: ' + msg.toString()); //echo\r
+      // server.close();\r
+      ws.ack = true;\r
+    });\r
+    ws.on('ping', function (msg) {\r
+      console.log('Ping received: %s', msg.toString());\r
+    });\r
+    ws.on('error', function (msg) {\r
+      console.log('Error: %s', msg.toString());\r
+    });\r
+\r
+    var i = 0;\r
+    var prev = 0;\r
+    var timeout = setInterval(function() {\r
+      if (!ws.ack)\r
+        return false;\r
+      var now = Date.now();\r
+      var data;\r
+      try {\r
+        data = fs.readFileSync('/tmp/latest.jpg');\r
+      } catch (err) {\r
+        data = fs.readFileSync('/opt/home/dashboard/default.gif');\r
+      }\r
+      ws.send(data, {mask: false, binary: true});\r
+      console.log(`Sending frame(${i++}), interval(${now - prev} ms)`);\r
+      // server.broadcast(data, {mask: false, binary: true});\r
+      // server.broadcast(`HELLO TO ALL FROM IoT.js!!! (${i++}, ${now - prev})`);\r
+      prev = now;\r
+      ws.ack = false;\r
+    }, 1000 / 15.0);\r
+\r
+    ws.on('close', function (msg) {\r
+      console.log('Client close: ' + msg.reason + ' (' + msg.code + ')');\r
+      clearInterval(timeout);\r
+    });\r
+  };\r
+\r
+  server.on('error', function (msg) {\r
+    console.log('Error: %s', msg.toString());\r
+  });\r
+\r
+  server.on('close', function (msg) {\r
+    console.log('Server close: ' + msg.reason + ' (' + msg.code + ')');\r
+  });\r
+\r
+}\r
diff --git a/include/controller.h b/include/controller.h
new file mode 100644 (file)
index 0000000..571f998
--- /dev/null
@@ -0,0 +1,41 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTROLLER_H__
+#define __CONTROLLER_H__
+
+#define MV_RESULT_COUNT_MAX 30
+#define MV_RESULT_LENGTH_MAX (MV_RESULT_COUNT_MAX * 4) //4(x, y, w, h) * COUNT
+#define IMAGE_INFO_MAX ((8 * MV_RESULT_LENGTH_MAX) + 4)
+
+#define IMAGE_WIDTH 320
+#define IMAGE_HEIGHT 240
+#define CAMERA_IMAGE_QUALITY 100 //1~100
+#define CAMERA_PREVIEW_INTERVAL_MIN 50
+
+//카메라와 모터에 따라 최적화 필요한 값 --------------------------------
+#define SERVO_MOTOR_VERTICAL_MIN 20
+#define SERVO_MOTOR_VERTICAL_MAX 45
+#define SERVO_MOTOR_VERTICAL_CENTER ((SERVO_MOTOR_VERTICAL_MIN + SERVO_MOTOR_VERTICAL_MAX) / 2)
+#define SERVO_MOTOR_HORIZONTAL_MIN 30
+#define SERVO_MOTOR_HORIZONTAL_MAX 75
+#define SERVO_MOTOR_HORIZONTAL_CENTER ((SERVO_MOTOR_HORIZONTAL_MIN + SERVO_MOTOR_HORIZONTAL_MAX) / 2)
+
+// 70CM 앞 물체가 화면의 서보모터 이동단위(1/20) 만큼 이동에 필요한 값 (실측)
+#define SERVO_MOTOR_VERTICAL_STEP 1
+#define SERVO_MOTOR_HORIZONTAL_STEP 1.25
+
+#endif
diff --git a/include/controller_image.h b/include/controller_image.h
new file mode 100644 (file)
index 0000000..75e5e8e
--- /dev/null
@@ -0,0 +1,27 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTROLLER_IMAGE_H__
+#define __CONTROLLER_IMAGE_H__
+
+void controller_image_initialize(void);
+void controller_image_finalize(void);
+int controller_image_save_image_file(const char *path,
+       unsigned int width, unsigned int height, const unsigned char *buffer,
+       const char *comment, unsigned int comment_len);
+int controller_image_read_image_file(const char *path,
+       unsigned int *width, unsigned int *height, unsigned char *buffer, unsigned long long *size);
+#endif
diff --git a/include/controller_mv.h b/include/controller_mv.h
new file mode 100644 (file)
index 0000000..3b9b20f
--- /dev/null
@@ -0,0 +1,30 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTROLLER_MV_H__
+#define __CONTROLLER_MV_H__
+#include <mv_common.h>
+
+typedef void (*movement_detected_cb)(int horizontal, int vertical, int result[], int result_count, void *user_data);
+
+mv_source_h controller_mv_create_source(
+               unsigned char *buffer, unsigned int size,
+               unsigned int width, unsigned int height, mv_colorspace_e colorspace);
+void controller_mv_push_source(mv_source_h source);
+int controller_mv_set_movement_detection_event_cb(movement_detected_cb movement_detected_cb, void *user_data);
+void controller_mv_unset_movement_detection_event_cb(void);
+
+#endif
diff --git a/include/exif.h b/include/exif.h
new file mode 100644 (file)
index 0000000..5559187
--- /dev/null
@@ -0,0 +1,29 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __APP_EXIF_H__
+#define __APP_EXIF_H__
+
+/* CAUTION
+ * This function is only for adding exif with user comment
+ * to JPEG which has No existing exif data !!!
+ */
+int exif_write_jpg_file_with_comment(const char *output_file,
+               const unsigned char *jpg_data, unsigned int jpg_size,
+               unsigned int jpg_width, unsigned int jpg_height,
+               const char *comment, unsigned int comment_len);
+
+#endif /* __APP_EXIF_H__ */
diff --git a/include/log.h b/include/log.h
new file mode 100644 (file)
index 0000000..faf335a
--- /dev/null
@@ -0,0 +1,94 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LOG_H__
+#define __LOG_H__
+
+#include <strings.h>
+#include <dlog.h>
+
+#ifdef  LOG_TAG
+#undef  LOG_TAG
+#endif
+#define LOG_TAG "SIV"
+
+#if !defined(_D)
+#define _D(fmt, arg...) dlog_print(DLOG_DEBUG, LOG_TAG, "[%s][%s:%d] " fmt "\n", rindex(__FILE__, '/') + 1, __func__, __LINE__, ##arg)
+#endif
+
+#if !defined(_I)
+#define _I(fmt, arg...) dlog_print(DLOG_INFO, LOG_TAG, "[%s][%s:%d] " fmt "\n", rindex(__FILE__, '/') + 1, __func__, __LINE__, ##arg)
+#endif
+
+#if !defined(_W)
+#define _W(fmt, arg...) dlog_print(DLOG_WARN, LOG_TAG, "[%s][%s:%d] " fmt "\n", rindex(__FILE__, '/') + 1, __func__, __LINE__, ##arg)
+#endif
+
+#if !defined(_E)
+#define _E(fmt, arg...) dlog_print(DLOG_ERROR, LOG_TAG, "[%s][%s:%d] " fmt "\n", rindex(__FILE__, '/') + 1, __func__, __LINE__, ##arg)
+#endif
+
+#define retvm_if(expr, val, fmt, arg...) do { \
+       if (expr) { \
+               _E(fmt, ##arg); \
+               _E("(%s) -> %s() return", #expr, __FUNCTION__); \
+               return val; \
+       } \
+} while (0)
+
+#define retv_if(expr, val) do { \
+       if (expr) { \
+               _E("(%s) -> %s() return", #expr, __FUNCTION__); \
+               return (val); \
+       } \
+} while (0)
+
+#define retm_if(expr, fmt, arg...) do { \
+       if (expr) { \
+               _E(fmt, ##arg); \
+               _E("(%s) -> %s() return", #expr, __FUNCTION__); \
+               return; \
+       } \
+} while (0)
+
+#define ret_if(expr) do { \
+       if (expr) { \
+               _E("(%s) -> %s() return", #expr, __FUNCTION__); \
+               return; \
+       } \
+} while (0)
+
+#define goto_if(expr, val) do { \
+       if (expr) { \
+               _E("(%s) -> goto", #expr); \
+               goto val; \
+       } \
+} while (0)
+
+#define break_if(expr) { \
+       if (expr) { \
+               _E("(%s) -> break", #expr); \
+               break; \
+       } \
+}
+
+#define continue_if(expr) { \
+       if (expr) { \
+               _E("(%s) -> continue", #expr); \
+               continue; \
+       } \
+}
+#endif
diff --git a/include/motion.h b/include/motion.h
new file mode 100644 (file)
index 0000000..8acd48c
--- /dev/null
@@ -0,0 +1,29 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MOTION_H__
+#define __MOTION_H__
+
+typedef void(*motion_state_changed_cb) (int state, void* user_data);
+
+int motion_initialize(void);
+int motion_finalize(void);
+int motion_state_set(int state, const char *pass_key);
+int motion_state_get(int *state);
+int motion_state_changed_cb_set(
+       const char *callback_key, motion_state_changed_cb callback, void *cb_data);
+
+#endif /* __MOTION_H__ */
diff --git a/include/resource_camera.h b/include/resource_camera.h
new file mode 100644 (file)
index 0000000..8316c97
--- /dev/null
@@ -0,0 +1,37 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __RESOURCE_CAMERA_H__
+#define __RESOURCE_CAMERA_H__
+
+typedef struct __image_buffer_data_s {
+    unsigned char *buffer;
+       unsigned int buffer_size;
+       unsigned int image_width;
+       unsigned int image_height;
+       camera_pixel_format_e format;
+       void *user_data;
+} image_buffer_data_s;
+
+typedef void (*preview_image_buffer_created_cb)(void *buffedata);
+typedef void (*capture_completed_cb)(const void *image, unsigned int size, void *user_data);
+
+int resource_camera_init(preview_image_buffer_created_cb preview_image_buffer_created_cb, void *user_data);
+int resource_camera_start_preview(void);
+int resource_camera_capture(capture_completed_cb capture_completed_cb, void *data);
+void resource_camera_close(void);
+
+#endif
diff --git a/include/resource_servo_motor_sg90.h b/include/resource_servo_motor_sg90.h
new file mode 100644 (file)
index 0000000..724eab8
--- /dev/null
@@ -0,0 +1,40 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __RESOURCE_SERVO_MOTOR_SG90_H__
+#define __RESOURCE_SERVO_MOTOR_SG90_H__
+
+void resource_close_servo_motor(int channel);
+
+/**
+ * This module is sample codes to handling Servo motors in Tizen platform.
+ * Hardware is configured with SG90,
+ * @param[in] channel
+ * @param[in] duty_cycle_ms
+ * @return 0 on success, otherwise a negative error value
+  */
+int resource_set_servo_motor_sg90_value(int channel, double duty_cycle_ms);
+
+/**
+ * This module is sample codes to handling Servo motors in Tizen platform.
+ * Hardware is configured with SG90,
+ * @param[in] channel
+ * @param[in] percent
+ * @return 0 on success, otherwise a negative error value
+  */
+int resource_rotate_servo_motor_by_percent(int channel, double percent);
+
+#endif /* __RESOURCE_SERVO_MOTOR_SG90_H__ */
diff --git a/include/servo-h.h b/include/servo-h.h
new file mode 100644 (file)
index 0000000..d9c5517
--- /dev/null
@@ -0,0 +1,29 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SERVO_H_H__
+#define __SERVO_H_H__
+
+#include "servo-type.h"
+
+int servo_h_initialize(void);
+int servo_h_finalize(void);
+int servo_h_state_set(double value, const char *pass_key);
+int servo_h_state_get(double *value);
+int servo_h_state_changed_cb_set(
+       const char *callback_key, servo_state_changed_cb callback, void *cb_data);
+
+#endif /* __SERVO_H_H__ */
diff --git a/include/servo-type.h b/include/servo-type.h
new file mode 100644 (file)
index 0000000..78ed7d2
--- /dev/null
@@ -0,0 +1,22 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SERVO_TYPE_H__
+#define __SERVO_TYPE_H__
+
+typedef void(*servo_state_changed_cb) (double value, void* user_data);
+
+#endif /* __SERVO_TYPE_H__ */
diff --git a/include/servo-v.h b/include/servo-v.h
new file mode 100644 (file)
index 0000000..80e3e23
--- /dev/null
@@ -0,0 +1,29 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SERVO_V_H__
+#define __SERVO_V_H__
+
+#include "servo-type.h"
+
+int servo_v_initialize(void);
+int servo_v_finalize(void);
+int servo_v_state_set(double value, const char *pass_key);
+int servo_v_state_get(double *value);
+int servo_v_state_changed_cb_set(
+       const char *callback_key, servo_state_changed_cb callback, void *cb_data);
+
+#endif /* __SERVO_V_H__ */
diff --git a/include/smartthings.h b/include/smartthings.h
new file mode 100644 (file)
index 0000000..f1b6edc
--- /dev/null
@@ -0,0 +1,1658 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_H__
+#define __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_H__
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <tizen.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup CAPI_SMARTTHINGS_THING_MASTER_MODULE
+ * @{
+ */
+
+/**
+ * @brief Definition for the max length of SSID for access point.
+ * @since_ses 1
+ */
+#define SMARTTHINGS_SSID_LEN_MAX 32
+
+/**
+ * @brief Definition for the max length of cloud information.
+ * @since_ses 1
+ */
+#define SMARTTHINGS_CLOUD_INFO_LEN_MAX 128
+
+/**
+ * @brief Enumeration for the SmartThings error.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_ERROR_NONE = TIZEN_ERROR_NONE,                                /**< Successful */
+       SMARTTHINGS_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER,      /**< Invalid parameter */
+       SMARTTHINGS_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY,              /**< Out of memory */
+       SMARTTHINGS_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED,      /**< Permission denied */
+       SMARTTHINGS_ERROR_NO_DATA = TIZEN_ERROR_NO_DATA,                          /**< No data */
+       SMARTTHINGS_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED,              /**< Not supported */
+       SMARTTHINGS_ERROR_OPERATION_FAILED = TIZEN_ERROR_UNKNOWN - 1,             /**< Operation failed */
+       SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE = TIZEN_ERROR_UNKNOWN -2            /**< Service unavailable */
+} smartthings_error_e;
+
+/**
+ * @brief Enumeration for SmartThings status.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_STATUS_NOT_READY = -1,                   /**< Service agent is not ready */
+       SMARTTHINGS_STATUS_INIT = 0,                         /**< Initial state of SmartThings Thing */
+       SMARTTHINGS_STATUS_ES_STARTED,                       /**< Easy-setup is started */
+       SMARTTHINGS_STATUS_ES_DONE,                          /**< Easy-setup is done */
+       SMARTTHINGS_STATUS_ES_FAILED_ON_OWNERSHIP_TRANSFER,  /**< Easy-setup failed due to Ownership-Transfer failure */
+       SMARTTHINGS_STATUS_CONNECTING_TO_AP,                 /**< Connecting to target Wi-Fi access point */
+       SMARTTHINGS_STATUS_CONNECTED_TO_AP,                  /**< Connected to target Wi-Fi access point */
+       SMARTTHINGS_STATUS_CONNECTING_TO_AP_FAILED,          /**< Failed to connect to target Wi-Fi access point */
+       SMARTTHINGS_STATUS_REGISTERING_TO_CLOUD,             /**< Trying to sign up, sign in and publish resources to cloud */
+       SMARTTHINGS_STATUS_REGISTERED_TO_CLOUD,              /**< Publish resources to cloud is complete. Now the thing is ready to be controlled via cloud */
+       SMARTTHINGS_STATUS_REGISTERING_FAILED_ON_SIGN_IN,    /**< Failed to sign in to cloud */
+       SMARTTHINGS_STATUS_REGISTERING_FAILED_ON_PUB_RES     /**< Failed to publish resources to cloud */
+} smartthings_status_e;
+
+/**
+ * @brief Enumeration for RPC connection status.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_CONNECTION_STATUS_CONNECTED = 0,            /**< Connection is connected */
+       SMARTTHINGS_CONNECTION_STATUS_DISCONNECTED,             /**< Connection is disconnected */
+       SMARTTHINGS_CONNECTION_STATUS_REJECTED,                 /**< Connection is rejected */
+} smartthings_connection_status_e;
+
+/**
+ * @brief The Wi-Fi mode.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_WIFI_MODE_11A = (1 << 0),   /**< Wi-Fi 11A */
+       SMARTTHINGS_WIFI_MODE_11B = (1 << 1),   /**< Wi-Fi 11B */
+       SMARTTHINGS_WIFI_MODE_11G = (1 << 2),   /**< Wi-Fi 11G */
+       SMARTTHINGS_WIFI_MODE_11N = (1 << 3),   /**< Wi-Fi 11N */
+       SMARTTHINGS_WIFI_MODE_11AC = (1 << 4)   /**< Wi-Fi 11AC */
+} smartthings_wifi_mode_e;
+
+/**
+ * @brief The Wi-Fi frequency band.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_WIFI_FREQ_24G = (1 << 0),       /**< Wi-Fi 2.4GHz */
+       SMARTTHINGS_WIFI_FREQ_5G = (1 << 1),        /**< Wi-Fi 5GHz */
+} smartthings_wifi_freq_e;
+
+/**
+ * @brief The Wi-Fi authentication type of access point.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_WIFI_AUTHTYPE_NONE = 0, /**< No authentication */
+       SMARTTHINGS_WIFI_AUTHTYPE_WEP,      /**< WEP */
+       SMARTTHINGS_WIFI_AUTHTYPE_WPA_PSK,  /**< WPA-PSK */
+       SMARTTHINGS_WIFI_AUTHTYPE_WPA2_PSK  /**< WPA2-PSK */
+} smartthings_wifi_authtype_e;
+
+/**
+ * @brief The Wi-Fi encryption type of access point.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_WIFI_ENCTYPE_NONE = 0,      /**< No encryption */
+       SMARTTHINGS_WIFI_ENCTYPE_WEP_64,        /**< WEP 64 */
+       SMARTTHINGS_WIFI_ENCTYPE_WEP_128,       /**< WEP 128 */
+       SMARTTHINGS_WIFI_ENCTYPE_TKIP,          /**< TKIP */
+       SMARTTHINGS_WIFI_ENCTYPE_AES,           /**< AES */
+       SMARTTHINGS_WIFI_ENCTYPE_TKIP_AES       /**< TKIP/AES */
+} smartthings_wifi_enctype_e;
+
+/**
+ * @brief The SmartThings handle.
+ * @since_ses 1
+ */
+typedef struct smartthings_s *smartthings_h;
+
+/**
+ * @brief The access point information handle.
+ * @since_ses 1
+ */
+typedef struct smartthings_ap_info_s *smartthings_ap_info_h;
+
+/**
+ * @brief The device provisioning information handle.
+ * @since_ses 1
+ */
+typedef struct smartthings_device_prov_info_s *smartthings_device_prov_info_h;
+
+/**
+ * @brief The cloud information handle for cloud sign-up.
+ * @since_ses 1
+ */
+typedef struct smartthings_cloud_info_s *smartthings_cloud_info_h;
+
+/**
+ * @brief The access point list handle.
+ * @since_ses 1
+ */
+typedef struct smartthings_ap_list_s *smartthings_ap_list_h;
+
+/**
+ * @brief Callback for status of connection to SmartThings Thing agent.
+ * @details The following error codes can be received: \n
+       #SMARTTHINGS_ERROR_NONE:                               Success \n
+       #SMARTTHINGS_ERROR_PERMISSION_DENIED:      Permission denied \n
+       #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE:   Service unavailable \n
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user can see result as #smartthings_error_e enumeration value.
+ * @remarks When callback is called, user can see connection status as #smartthings_connection_status_e enumeration value.
+ *
+ * @param[in] result The result of connection operation
+ * @param[in] handle The SmartThings handle
+ * @param[in] status The status of connection
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_initialize()
+ */
+typedef void (*smartthings_connection_status_cb)(smartthings_error_e result, smartthings_h handle, smartthings_connection_status_e status, void *user_data);
+
+/**
+ * @brief Callback for SmartThings Thing status.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user can see SmartThings status as #smartthings_status_e enumeration value.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] status The status of SmartThings
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_status_changed_cb()
+ * @see smartthings_unset_status_changed_cb()
+ */
+typedef void (*smartthings_status_changed_cb)(smartthings_h handle, smartthings_status_e status, void *user_data);
+
+/**
+ * @brief Callback for getting user's input regarding mutual verification.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user can send a confirmation for mutual verification as true or false.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_user_confirm_cb()
+ * @see smartthings_unset_user_confirm_cb()
+ */
+typedef void (*smartthings_user_confirm_cb)(smartthings_h handle, void *user_data);
+
+/**
+ * @brief Callback for getting user's opinion regarding device reset.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user can sends a confirmation for reset as true or false.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_reset_confirm_cb()
+ * @see smartthings_unset_reset_confirm_cb()
+ */
+typedef void (*smartthings_reset_confirm_cb)(smartthings_h handle, void *user_data);
+
+/**
+ * @brief Callback for result of reset operation.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user can check reset operation succeeds or fails.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] result The result of reset
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_reset_result_cb()
+ * @see smartthings_unset_reset_result_cb()
+ */
+typedef void (*smartthings_reset_result_cb)(smartthings_h handle, bool result, void *user_data);
+
+/**
+ * @brief Callback for carrying the randomly generated PIN information.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks The @a pin can be used only in the callback. To use outside, make a copy.
+ * @remarks When callback is called, user can see PIN value and length.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] pin The PIN data in string format
+ * @param[in] size The PIN length of @a pin
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_pin_cb()
+ * @see smartthings_unset_pin_cb()
+ */
+typedef void (*smartthings_pin_generated_cb)(smartthings_h handle, const char* pin, size_t size, void *user_data);
+
+/**
+ * @brief Callback for informing the application to close the PIN display.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user can know PIN based ownership transfer is finished.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_pin_cb()
+ * @see smartthings_unset_pin_cb()
+ */
+typedef void (*smartthings_pin_display_close_cb)(smartthings_h handle, void *user_data);
+
+/**
+ * @brief Callback for informing Wi-Fi AP information to connect.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks The @a ap_info_h should not be released.
+ * @remarks The @a ap_info_h will be released when smartthings_unset_wifi_ap_provisioning_cb() is called.
+ * @remarks When callback is called, user can get Wi-Fi provisioning information.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] ap_info_h The AP information handle
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_wifi_ap_provisioning_cb()
+ * @see smartthings_unset_wifi_ap_provisioning_cb()
+ */
+typedef void (*smartthings_wifi_ap_provisioning_cb)(smartthings_h handle, smartthings_ap_info_h ap_info_h, void *user_data);
+
+/**
+ * @brief Callback for informing device provisioning information.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks The @a dev_prov_h should not be released.
+ * @remarks The @a dev_prov_h will be released when smartthings_unset_device_provisioning_cb() is called.
+ * @remarks When callback is called, user can get device provisioning information.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] dev_prov_h The device provisioning information handle
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_device_provisioning_cb()
+ * @see smartthings_unset_device_provisioning_cb()
+ */
+typedef void (*smartthings_device_provisioning_cb)(smartthings_h handle, smartthings_device_prov_info_h dev_prov_h, void *user_data);
+
+/**
+ * @brief Callback for informing the scan AP list request.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user scans access points, sets AP list and sends it to agent.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] req_id The request ID
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_scan_ap_cb()
+ * @see smartthings_unset_scan_ap_cb()
+ */
+typedef void (*smartthings_scan_ap_cb)(smartthings_h handle, int req_id, void *user_data);
+
+/**
+ * @brief Callback for informing the stop soft AP request.
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user stops soft AP.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_set_stop_soft_ap_cb()
+ * @see smartthings_unset_stop_soft_ap_cb()
+ */
+typedef void (*smartthings_stop_soft_ap_cb)(smartthings_h handle, void *user_data);
+
+/**
+ * @brief Creates a handle and connects to agent.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks The @a handle must be released using smartthings_deinitialize().
+ * @remarks Ths function returns #SMARTTHINGS_ERROR_PERMISSION_DENIED\n
+ *          if the application has no app-defined privilege for 'http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master'.
+ *
+ * @param[out] handle The SmartThings handle to be newly created on success
+ * @param[in] connection_status_cb The RPC connection status callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_NOT_SUPPORTED Not supported
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_deinitialize()
+ */
+int smartthings_initialize(smartthings_h *handle,
+                                               smartthings_connection_status_cb connection_status_cb,
+                                               void *user_data);
+
+/**
+ * @brief Deinitializes a handle and disconnects from the agent.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_initialize()
+ */
+int smartthings_deinitialize(smartthings_h handle);
+
+/**
+ * @brief Starts SmartThings Thing operation.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_stop()
+ */
+int smartthings_start(smartthings_h handle);
+
+/**
+ * @brief Stops SmartThings Thing operation.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_start()
+ */
+int smartthings_stop(smartthings_h handle);
+
+/**
+ * @brief Sets thing status changed callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks Callback is called when SmartThings status is changed.
+ * @remarks When callback is called, user can get SmartThings status as #smartthings_status_e enumeration value.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] status_cb The status changed callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_status_changed_cb()
+ */
+int smartthings_set_status_changed_cb(smartthings_h handle,
+                                                               smartthings_status_changed_cb status_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets thing status changed callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_status_changed_cb()
+ */
+int smartthings_unset_status_changed_cb(smartthings_h handle);
+
+/**
+ * @brief Sets test certificate files.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks These files should be placed in 'res' directory of application.
+ * @remarks This function is needed only for using test certificate.
+ * @remarks This function can be used before smartthings_start()
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] certificate The certificate file
+ * @param[in] private_key The private key file
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_set_certificate_file(smartthings_h handle, const char *certificate, const char *private_key);
+
+/**
+ * @brief Sets device property for Easy-setup.
+ * @since_ses 1
+ *
+ * @remarks This function can be used before smartthings_start()
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] dev_name The device name
+ * @param[in] wifi_mode The supported Wi-Fi mode (bit masked value for #smartthings_wifi_mode_e)
+ * @param[in] wifi_freq The supported Wi-Fi frequency (bit masked value for #smartthings_wifi_freq_e)
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_set_device_property(smartthings_h handle, const char* dev_name, int wifi_mode, int wifi_freq);
+
+/**
+ * @brief Gets a device ID.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks The @a device_id should be released using free().
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[out] device_id The device ID
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_get_device_id(smartthings_h handle, char **device_id);
+
+/**
+ * @brief Gets a Easy-setup status.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[out] is_completed The status of Easy-setup whether it is completed or not
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_get_easysetup_status(smartthings_h handle, bool *is_completed);
+
+/**
+ * @brief Starts Easy-setup mode.
+ *
+ * @details This function requests for turning on soft AP to SmartThings Thing agent.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/softap
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_stop_easysetup()
+ */
+int smartthings_start_easysetup(smartthings_h handle);
+
+/**
+ * @brief Stops Easy-setup mode.
+ *
+ * @details This function requests for turning off soft AP to SmartThings Thing agent.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/softap
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_start_easysetup()
+ */
+int smartthings_stop_easysetup(smartthings_h handle);
+
+/**
+ * @brief Sets callback for getting user confirmation for mutual verification based just work ownership transfer.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks Callback is called when it needs user's confirm for mutual verification based just work ownership transfer.
+ * @remarks When callback is called, user can send a confirmation for mutual verification based just work ownership transfer as true or false.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] confirm_cb The user confirm callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_user_confirm_cb()
+ */
+int smartthings_set_user_confirm_cb(smartthings_h handle,
+                                                               smartthings_user_confirm_cb confirm_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets user confirmation callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_user_confirm_cb()
+ */
+int smartthings_unset_user_confirm_cb(smartthings_h handle);
+
+/**
+ * @brief Sets reset confirmation callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks Callback is called when it needs user's confirm for reset.
+ * @remarks When callback is called, user can send a confirmation for reset as true or false.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] confirm_cb The reset confirm callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_reset_confirm_cb()
+ */
+int smartthings_set_reset_confirm_cb(smartthings_h handle,
+                                                               smartthings_reset_confirm_cb confirm_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets reset confirmation callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_reset_confirm_cb()
+ */
+int smartthings_unset_reset_confirm_cb(smartthings_h handle);
+
+/**
+ * @brief Sets reset result callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks Callback is called when reset operation returns its result.
+ * @remarks When callback is called, user can check reset operation succeeds or fails.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] reset_result_cb The reset result callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_reset_result_cb()
+ */
+int smartthings_set_reset_result_cb(smartthings_h handle,
+                                                               smartthings_reset_result_cb reset_result_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets reset result callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_reset_result_cb()
+ */
+int smartthings_unset_reset_result_cb(smartthings_h handle);
+
+
+/**
+ * @brief Sets callback for getting randomly generated PIN for the PIN-based ownership transfer request.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks @a generated_cb callback is called when PIN is generated.
+ * @remarks @a close_cb callback is called when PIN based ownership transfer is finished.
+ * @remarks When @a generated_cb callback is called, user can see PIN value and length.
+ * @remarks When @a close_cb callback is called, user can know PIN based ownership transfer is finished.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] generated_cb The PIN generation callback to register
+ * @param[in] close_cb The PIN display close callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_pin_cb()
+ */
+int smartthings_set_pin_cb(smartthings_h handle,
+                                                               smartthings_pin_generated_cb generated_cb,
+                                                               smartthings_pin_display_close_cb close_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets PIN callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_pin_cb()
+ */
+int smartthings_unset_pin_cb(smartthings_h handle);
+
+/**
+ * @brief Sets callback for getting Wi-Fi AP information during Easy-setup.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks Callback is called when Wi-Fi provisioning event occurs.
+ * @remarks When callback is called, user can get Wi-Fi provisioning information.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] wifi_ap_cb The Wi-Fi AP callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_wifi_ap_provisioning_cb()
+ */
+int smartthings_set_wifi_ap_provisioning_cb(smartthings_h handle,
+                                                               smartthings_wifi_ap_provisioning_cb wifi_ap_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets callback for getting Wi-Fi AP information during Easy-setup.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_wifi_ap_provisioning_cb()
+ */
+int smartthings_unset_wifi_ap_provisioning_cb(smartthings_h handle);
+
+/**
+ * @brief Sets callback for getting device provisioning information.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks Callback is called when device provisioning event occurs.
+ * @remarks When callback is called, user can get device provisioning information.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] dev_prov_cb The device provisioning callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_device_provisioning_cb()
+ */
+int smartthings_set_device_provisioning_cb(smartthings_h handle,
+                                                               smartthings_device_provisioning_cb dev_prov_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets callback for getting device provisioning information.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_device_provisioning_cb()
+ */
+int smartthings_unset_device_provisioning_cb(smartthings_h handle);
+
+/**
+ * @brief Sets callback for informing the scan AP list request.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks Callback is called when GET request for access point list.
+ * @remarks When callback is called, user scans access points, sets AP list and sends it to agent.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] scan_ap_cb The callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_scan_ap_cb()
+ * @see smartthings_send_ap_list()
+ */
+int smartthings_set_scan_ap_cb(smartthings_h handle,
+                                                               smartthings_scan_ap_cb scan_ap_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets callback for informing the scan AP list request.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_scan_ap_cb()
+ */
+int smartthings_unset_scan_ap_cb(smartthings_h handle);
+
+/**
+ * @brief Sets callback for informing the stop soft AP request.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks Only one callback function can be set with this function.
+ * @remarks If multiple callbacks are set, the last one is registered only.
+ * @remarks Callback is called when POST request for stopping soft AP.
+ * @remarks When callback is called, user stops soft AP.
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] stop_soft_ap_cb The callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_unset_stop_soft_ap_cb()
+ */
+int smartthings_set_stop_soft_ap_cb(smartthings_h handle,
+                                                               smartthings_stop_soft_ap_cb stop_soft_ap_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets callback for informing the stop soft AP request.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_stop_soft_ap_cb()
+ */
+int smartthings_unset_stop_soft_ap_cb(smartthings_h handle);
+
+/**
+ * @brief Sends a user confirmation for MUTUAL VERIFICATION BASED JUST WORK Ownership transfer.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] confirm The user confirmation for OTM(ownership transfer method)
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_user_confirm_cb()
+ * @see smartthings_unset_user_confirm_cb()
+ */
+int smartthings_send_user_confirm(smartthings_h handle, bool confirm);
+
+
+/**
+ * @brief Sends a reset confirmation.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] confirm The reset confirmation
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_reset_confirm_cb()
+ * @see smartthings_unset_reset_confirm_cb()
+ */
+int smartthings_send_reset_confirm(smartthings_h handle, bool confirm);
+
+/**
+ * @brief Sends a reset command for resetting the device's Cloud signup and Easy-setup.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] handle The SmartThings handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_set_reset_result_cb()
+ * @see smartthings_unset_reset_result_cb()
+ */
+int smartthings_reset(smartthings_h handle);
+
+/**
+ * @brief Gets SSID of access point.
+ * @since_ses 1
+ *
+ * @remarks The @a ssid should be released using free().
+ *
+ * @param[in] ap_info_h The SmartThings AP information handle
+ * @param[out] ssid The SSID name
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_set_wifi_ap_provisioning_cb()
+ * @see smartthings_unset_wifi_ap_provisioning_cb()
+ */
+int smartthings_apinfo_get_ssid(smartthings_ap_info_h ap_info_h, char **ssid);
+
+/**
+ * @brief Gets password of access point.
+ * @since_ses 1
+ *
+ * @remarks The @a pwd should be released using free().
+ *
+ * @param[in] ap_info_h The SmartThings AP information handle
+ * @param[out] pwd The password
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_set_wifi_ap_provisioning_cb()
+ * @see smartthings_unset_wifi_ap_provisioning_cb()
+ */
+int smartthings_apinfo_get_password(smartthings_ap_info_h ap_info_h, char **pwd);
+
+/**
+ * @brief Gets authentification type of access point.
+ * @since_ses 1
+ *
+ * @param[in] ap_info_h The SmartThings AP information handle
+ * @param[out] authtype The authentification type
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_set_wifi_ap_provisioning_cb()
+ * @see smartthings_unset_wifi_ap_provisioning_cb()
+ */
+int smartthings_apinfo_get_authtype(smartthings_ap_info_h ap_info_h, smartthings_wifi_authtype_e *authtype);
+
+/**
+ * @brief Gets encryption type of access point.
+ * @since_ses 1
+ *
+ * @param[in] ap_info_h The SmartThings AP information handle
+ * @param[out] enctype The encryption type
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_set_wifi_ap_provisioning_cb()
+ * @see smartthings_unset_wifi_ap_provisioning_cb()
+ */
+int smartthings_apinfo_get_enctype(smartthings_ap_info_h ap_info_h, smartthings_wifi_enctype_e *enctype);
+
+/**
+ * @brief Gets channel information of access point.
+ * @since_ses 1
+ *
+ * @param[in] ap_info_h The SmartThings AP information handle
+ * @param[out] channel The frequency channel
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_set_wifi_ap_provisioning_cb()
+ * @see smartthings_unset_wifi_ap_provisioning_cb()
+ */
+int smartthings_apinfo_get_channel(smartthings_ap_info_h ap_info_h, int *channel);
+
+/**
+ * @brief Gets language of device provisioing information.
+ * @since_ses 1
+ *
+ * @remarks The @a language should be released using free().
+ *
+ * @param[in] dev_prov_h The SmartThings device provisioning information handle
+ * @param[out] language The IETF language tag using ISO 639X
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_set_device_provisioning_cb()
+ * @see smartthings_unset_device_provisioning_cb()
+ */
+int smartthings_devinfo_get_language(smartthings_device_prov_info_h dev_prov_h, char **language);
+
+/**
+ * @brief Gets country of device provisioing information.
+ * @since_ses 1
+ *
+ * @remarks The @a country should be released using free().
+ *
+ * @param[in] dev_prov_h The SmartThings device provisioning information handle
+ * @param[out] country The ISO Country Code (ISO 3166-1 Alpha-2)
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_set_device_provisioning_cb()
+ * @see smartthings_unset_device_provisioning_cb()
+ */
+int smartthings_devinfo_get_country(smartthings_device_prov_info_h dev_prov_h, char **country);
+
+/**
+ * @brief Gets datetime of device provisioing information.
+ * @since_ses 1
+ *
+ * @remarks The @a datetime should be released using free().
+ *
+ * @param[in] dev_prov_h The SmartThings device provisioning information handle
+ * @param[out] datetime The date and time
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_set_device_provisioning_cb()
+ * @see smartthings_unset_device_provisioning_cb()
+ */
+int smartthings_devinfo_get_datetime(smartthings_device_prov_info_h dev_prov_h, char **datetime);
+
+/**
+ * @brief Creates a SmartThings AP list handle.
+ * @since_ses 1
+ *
+ * @remarks The @a ap_list_h must be released using smartthings_aplist_destroy().
+ *
+ * @param[out] ap_list_h The SmartThings AP list handle to be newly created on success
+ * @param[in] count The count of AP
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OUT_OF_MEMORY Out of memory
+ *
+ * @see smartthings_aplist_destroy()
+ */
+int smartthings_aplist_create(smartthings_ap_list_h *ap_list_h, unsigned int count);
+
+/**
+ * @brief Destroys a SmartThings AP list handle.
+ * @since_ses 1
+ *
+ * @param[in] ap_list_h The SmartThings AP list handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_aplist_create()
+ */
+int smartthings_aplist_destroy(smartthings_ap_list_h ap_list_h);
+
+/**
+ * @brief Sets SSID at the specific index of AP list handle.
+ * @since_ses 1
+ *
+ * @param[in] ap_list_h The SmartThings AP list handle
+ * @param[in] idx The index
+ * @param[in] ssid The SSID of AP
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_aplist_create()
+ * @see smartthings_aplist_destroy()
+ */
+int smartthings_aplist_set_ssid(smartthings_ap_list_h ap_list_h, int idx, const char *ssid);
+
+/**
+ * @brief Sets BSSID at the specific index of AP list handle.
+ * @since_ses 1
+ *
+ * @param[in] ap_list_h The SmartThings AP list handle
+ * @param[in] idx The index
+ * @param[in] bssid The BSSID of AP
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_aplist_create()
+ * @see smartthings_aplist_destroy()
+ */
+int smartthings_aplist_set_bssid(smartthings_ap_list_h ap_list_h, int idx, const char *bssid);
+
+/**
+ * @brief Sets authentification type at the specific index of AP list handle.
+ * @since_ses 1
+ *
+ * @param[in] ap_list_h The SmartThings AP list handle
+ * @param[in] idx The index
+ * @param[in] authtype The authentification type of AP
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_aplist_create()
+ * @see smartthings_aplist_destroy()
+ */
+int smartthings_aplist_set_authtype(smartthings_ap_list_h ap_list_h, int idx, smartthings_wifi_authtype_e authtype);
+
+/**
+ * @brief Sets encryption type at the specific index of AP list handle.
+ * @since_ses 1
+ *
+ * @param[in] ap_list_h The SmartThings AP list handle
+ * @param[in] idx The index
+ * @param[in] enctype The encryption type of AP
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_aplist_create()
+ * @see smartthings_aplist_destroy()
+ */
+int smartthings_aplist_set_enctype(smartthings_ap_list_h ap_list_h, int idx, smartthings_wifi_enctype_e enctype);
+
+/**
+ * @brief Sets frequency channel at the specific index of AP list handle.
+ * @since_ses 1
+ *
+ * @param[in] ap_list_h The SmartThings AP list handle
+ * @param[in] idx The index
+ * @param[in] channel The frequency channel of AP
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_aplist_create()
+ * @see smartthings_aplist_destroy()
+ */
+int smartthings_aplist_set_channel(smartthings_ap_list_h ap_list_h, int idx, int channel);
+
+/**
+ * @brief Sets signal level at the specific index of AP list handle.
+ * @since_ses 1
+ *
+ * @param[in] ap_list_h The SmartThings AP list handle
+ * @param[in] idx The index
+ * @param[in] signal_level The signal level of AP
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_aplist_create()
+ * @see smartthings_aplist_destroy()
+ */
+int smartthings_aplist_set_signal_level(smartthings_ap_list_h ap_list_h, int idx, int signal_level);
+
+/**
+ * @brief Sets max speed rate at the specific index of AP list handle.
+ * @since_ses 1
+ *
+ * @param[in] ap_list_h The SmartThings AP list handle
+ * @param[in] idx The index
+ * @param[in] max_rate The max speed rate of AP
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_aplist_create()
+ * @see smartthings_aplist_destroy()
+ */
+int smartthings_aplist_set_max_rate(smartthings_ap_list_h ap_list_h, int idx, int max_rate);
+
+/**
+ * @brief Sends the scanned AP list.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] ap_list_h The SmartThings AP list handle
+ * @param[in] req_id The request ID
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_aplist_create()
+ * @see smartthings_aplist_destroy()
+ * @see smartthings_set_scan_ap_cb()
+ */
+int smartthings_send_ap_list(smartthings_h handle, smartthings_ap_list_h ap_list_h, int req_id);
+
+/**
+ * @brief Creates a SmartThings cloud information handle.
+ * @since_ses 1
+ *
+ * @remarks The @a cloud_info_h must be released using smartthings_cloudinfo_destroy().
+ *
+ * @param[out] cloud_info_h The SmartThings cloud information handle to be newly created on success
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_OUT_OF_MEMORY Out of memory
+ *
+ * @see smartthings_cloudinfo_destroy()
+ */
+int smartthings_cloudinfo_create(smartthings_cloud_info_h *cloud_info_h);
+
+/**
+ * @brief Destroys a SmartThings cloud information handle.
+ * @since_ses 1
+ *
+ * @param[in] cloud_info_h The SmartThings cloud information handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_cloudinfo_create()
+ */
+int smartthings_cloudinfo_destroy(smartthings_cloud_info_h cloud_info_h);
+
+/**
+ * @brief Sets region of cloud information.
+ * @since_ses 1
+ *
+ * @remarks The @a region can be set to one of "global" or "china".
+ * @remarks If it doesn't use this function, the @a region will be set to "global" internally.
+ *
+ * @param[in] cloud_info_h The SmartThings cloud information handle
+ * @param[in] region The region name
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_cloudinfo_set_region(smartthings_cloud_info_h cloud_info_h, const char *region);
+
+/**
+ * @brief Sets authentification provider of cloud information.
+ * @since_ses 1
+ *
+ * @param[in] cloud_info_h The SmartThings cloud information handle
+ * @param[in] auth_provider The authentification provider
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_cloudinfo_set_auth_provider(smartthings_cloud_info_h cloud_info_h, const char *auth_provider);
+
+/**
+ * @brief Sets access token of cloud information.
+ * @since_ses 1
+ *
+ * @param[in] cloud_info_h The SmartThings cloud information handle
+ * @param[in] access_token The access token
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_cloudinfo_set_access_token(smartthings_cloud_info_h cloud_info_h, const char *access_token);
+
+/**
+ * @brief Sets refresh token of cloud information.
+ * @since_ses 1
+ *
+ * @param[in] cloud_info_h The SmartThings cloud information handle
+ * @param[in] refresh_token The refresh token
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_cloudinfo_set_refresh_token(smartthings_cloud_info_h cloud_info_h, const char *refresh_token);
+
+/**
+ * @brief Sets user ID of cloud information.
+ * @since_ses 1
+ *
+ * @param[in] cloud_info_h The SmartThings cloud information handle
+ * @param[in] user_id The user ID
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_cloudinfo_set_user_id(smartthings_cloud_info_h cloud_info_h, const char *user_id);
+
+/**
+ * @brief Sets client ID of cloud information.
+ * @since_ses 1
+ *
+ * @param[in] cloud_info_h The SmartThings cloud information handle
+ * @param[in] client_id The client ID
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_cloudinfo_set_client_id(smartthings_cloud_info_h cloud_info_h, const char *client_id);
+
+/**
+ * @brief Requests to sign up to cloud.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] cloud_info_h The handle for cloud signup
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_sign_up_cloud(smartthings_h handle, smartthings_cloud_info_h cloud_info_h);
+
+/**
+ * @brief Sets preconfigured PIN.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] pin The PIN code to preconfigure
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_set_preconfigured_pin(smartthings_h handle, const char* pin);
+
+/**
+ * @brief Sets MOT(multiple ownership transfer) status.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] handle The SmartThings handle
+ * @param[in] enable The MOT(multiple ownership transfer) status
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_set_mot_status(smartthings_h handle, bool enable);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_H__ */
diff --git a/include/smartthings_notification.h b/include/smartthings_notification.h
new file mode 100644 (file)
index 0000000..57e1f28
--- /dev/null
@@ -0,0 +1,285 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_NOTIFICATION_H__
+#define __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_NOTIFICATION_H__
+
+#include <smartthings_resource.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file smartthings_notification.h
+ */
+
+/**
+ * @addtogroup CAPI_SMARTTHINGS_THING_NOTIFICATION_MODULE
+ * @{
+ */
+
+/**
+ * @brief Enumeration for the type of notification.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_NOTIFICATION_TYPE_ALERT = 1,    /**< Alert type */
+       SMARTTHINGS_NOTIFICATION_TYPE_NOTICE,       /**< Notice type */
+       SMARTTHINGS_NOTIFICATION_TYPE_EVENT,        /**< Event type */
+       SMARTTHINGS_NOTIFICATION_TYPE_INFO,         /**< Info type */
+       SMARTTHINGS_NOTIFICATION_TYPE_WARNING       /**< Warning type */
+} smartthings_notification_type_e;
+
+
+/**
+ * @brief The SmartThings push message handle.
+ * @since_ses 1
+ */
+typedef struct smartthings_push_msg_s* smartthings_push_msg_h;
+
+/**
+ * @brief Creates a SmartThings push message handle.
+ * @since_ses 1
+ *
+ * @remarks The @a handle must be released using smartthings_push_msg_destroy().
+ *
+ * @param[out] handle The SmartThings push message handle to be newly created on success
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_push_msg_destroy()
+ */
+int smartthings_push_msg_create(smartthings_push_msg_h *handle);
+
+/**
+ * @brief Destroys a SmartThings push message handle and releases all its resources.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings push message handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_push_msg_create()
+ */
+int smartthings_push_msg_destroy(smartthings_push_msg_h handle);
+
+/**
+ * @brief Sets a URI for the push message.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings push message handle
+ * @param[in] uri The URI of push message to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ */
+int smartthings_push_msg_set_uri(smartthings_push_msg_h handle, const char *uri);
+
+/**
+ * @brief Sets a type for the push message.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings push message handle
+ * @param[in] type The type of push message to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ */
+int smartthings_push_msg_set_type(smartthings_push_msg_h handle, smartthings_notification_type_e type);
+
+/**
+ * @brief Sets a code for the push message.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings push message handle
+ * @param[in] code The code of push message to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ */
+int smartthings_push_msg_set_code(smartthings_push_msg_h handle, const char *code);
+
+/**
+ * @brief Sets a payload for the push message.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings push message handle
+ * @param[in] payload The payload of push message to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ */
+int smartthings_push_msg_set_payload(smartthings_push_msg_h handle, smartthings_payload_h payload);
+
+/**
+ * @brief Sends the push message.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[in] push_msg_h The SmartThings push message handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ */
+int smartthings_send_push_msg(smartthings_resource_h st_h, smartthings_push_msg_h push_msg_h);
+
+
+/**
+ * @brief The SmartThings notification message handle.
+ * @since_ses 1
+ */
+typedef struct smartthings_notification_msg_s* smartthings_notification_msg_h;
+
+
+/**
+ * @brief Creates a SmartThings notification message handle.
+ * @since_ses 1
+ *
+ * @remarks The @a handle must be released using smartthings_notification_msg_destroy().
+ *
+ * @param[out] handle The SmartThings notification message handle to be newly created on success
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ *
+ * @see smartthings_notification_msg_destroy()
+ */
+int smartthings_notification_msg_create(smartthings_notification_msg_h *handle);
+
+/**
+ * @brief Destroys a SmartThings notification message handle and releases all its resources.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings notification message handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_notification_msg_create()
+ */
+int smartthings_notification_msg_destroy(smartthings_notification_msg_h handle);
+
+/**
+ * @brief Sets a URI for the notification message.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings notification message handle
+ * @param[in] uri The URI of notification message to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ */
+int smartthings_notification_msg_set_uri(smartthings_notification_msg_h handle, const char *uri);
+
+/**
+ * @brief Sets a type for the notification message.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings notification message handle
+ * @param[in] type The type of notification message to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ */
+int smartthings_notification_msg_set_type(smartthings_notification_msg_h handle, smartthings_notification_type_e type);
+
+/**
+ * @brief Sets a payload for the notification message.
+ * @since_ses 1
+ *
+ * @param[in] handle The SmartThings notification message handle
+ * @param[in] name The name of payload
+ * @param[in] payload The payload of notification message to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ */
+int smartthings_notification_msg_set_payload(smartthings_notification_msg_h handle, const char *name, smartthings_payload_h payload);
+
+/**
+ * @brief Sends the notification message.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[in] notification_msg_h The SmartThings notification message handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ */
+int smartthings_send_notification_msg(smartthings_resource_h st_h, smartthings_notification_msg_h notification_msg_h);
+
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_NOTIFICATION_H__ */
diff --git a/include/smartthings_payload.h b/include/smartthings_payload.h
new file mode 100644 (file)
index 0000000..b4612c1
--- /dev/null
@@ -0,0 +1,660 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_PAYLOAD_H__
+#define __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_PAYLOAD_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file smartthings_payload.h
+ */
+
+/**
+ * @addtogroup CAPI_SMARTTHINGS_THING_PAYLOAD_MODULE
+ * @{
+ */
+
+/**
+ * @brief The SmartThings payload handle.
+ * @since_ses 1
+ */
+typedef struct smartthings_payload_s *smartthings_payload_h;
+
+/**
+ * @brief Creates a SmartThings payload handle.
+ * @since_ses 1
+ *
+ * @remarks The @a payload must be released using smartthings_payload_destroy().
+ * @remarks If the @a payload is set to parent payload as an object or object array
+ *          using smartthings_payload_set_object() or smartthings_payload_set_object_array(),
+ *          the @a payload must not be released.
+
+ * @param[out] payload The SmartThings payload handle to be newly created on success
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ *
+ * @see smartthings_payload_destroy()
+ */
+int smartthings_payload_create(smartthings_payload_h *payload);
+
+/**
+ * @brief Destroys a SmartThings payload handle and releases all its resources.
+ * @since_ses 1
+ *
+ * @remarks If the @a payload has child payload, its child payload will be released as well.
+ *
+ * @param[in] payload The SmartThings payload handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_payload_create()
+ */
+int smartthings_payload_destroy(smartthings_payload_h payload);
+
+/**
+ * @brief Sets an integer value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing value for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] value The value to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_int(smartthings_payload_h payload, const char *attr_name, int value);
+
+/**
+ * @brief Sets a boolean value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing value for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] value The value to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_bool(smartthings_payload_h payload, const char *attr_name, bool value);
+
+/**
+ * @brief Sets a double value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing value for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] value The value to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_double(smartthings_payload_h payload, const char *attr_name, double value);
+
+/**
+ * @brief Sets a string value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing value for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] value The value to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_string(smartthings_payload_h payload, const char *attr_name, const char *value);
+
+/**
+ * @brief Sets a byte string value and length for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing value for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] value The value to set
+ * @param[in] length The size of value
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ */
+int smartthings_payload_set_byte_string(smartthings_payload_h payload, const char *attr_name, const char *value, unsigned int length);
+
+/**
+ * @brief Sets an object value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a value is SmartThings payload handle.
+ * @remarks The function replaces any existing value for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] value The value to set
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_object(smartthings_payload_h payload, const char *attr_name, smartthings_payload_h value);
+
+/**
+ * @brief Sets an integer array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing array for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] array The array associated with the given attribute name
+ * @param[in] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_int_array(smartthings_payload_h payload, const char *attr_name, const int *array, unsigned int length);
+
+/**
+ * @brief Sets a boolean array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing array for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] array The array associated with the given attribute name
+ * @param[in] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_bool_array(smartthings_payload_h payload, const char *attr_name, const bool *array, unsigned int length);
+
+/**
+ * @brief Sets a double array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing array for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] array The array associated with the given attribute name
+ * @param[in] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_double_array(smartthings_payload_h payload, const char *attr_name, const double *array, unsigned int length);
+
+/**
+ * @brief Sets a string array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing array for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] array The array associated with the given attribute name
+ * @param[in] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_set_string_array(smartthings_payload_h payload, const char *attr_name, const char **array, unsigned int length);
+
+/**
+ * @brief Sets an object array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The function replaces any existing array for the given @a attr_name.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[in] array The array associated with the given attribute name
+ * @param[in] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ */
+int smartthings_payload_set_object_array(smartthings_payload_h payload, const char *attr_name, smartthings_payload_h *array, unsigned int length);
+
+/**
+ * @brief Adds a payload for child resource of collection resource.
+ * @since_ses 1
+ *
+ * @remarks The @a resource_uri is key value, one of several child resources of the collection resource.
+ * @remarks The @a value is SmartThings payload handle.
+ * @remarks The function replaces any existing payload for the given @a resource_uri.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] resource_uri The resource URI of child resource
+ * @param[in] value The payload associated with the given resource URI
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int smartthings_payload_add_collection_object(smartthings_payload_h payload, const char *resource_uri, smartthings_payload_h value);
+
+/**
+ * @brief Gets an integer value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] value The value associated with the given attribute name
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ */
+int smartthings_payload_get_int(smartthings_payload_h payload, const char *attr_name, int *value);
+
+/**
+ * @brief Gets a boolean value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] value The value associated with the given attribute name
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ */
+int smartthings_payload_get_bool(smartthings_payload_h payload, const char *attr_name, bool *value);
+
+/**
+ * @brief Gets a double value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] value The value associated with the given attribute name
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ */
+int smartthings_payload_get_double(smartthings_payload_h payload, const char *attr_name, double *value);
+
+/**
+ * @brief Gets a string value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a value should be released using free().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] value The value associated with the given attribute name
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ */
+int smartthings_payload_get_string(smartthings_payload_h payload, const char *attr_name, char **value);
+
+/**
+ * @brief Gets a byte string value and length for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a value should be released using free().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] value The value associated with the given attribute name
+ * @param[out] length The size of value
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ */
+int smartthings_payload_get_byte_string(smartthings_payload_h payload, const char *attr_name, char **value, unsigned int *length);
+
+/**
+ * @brief Gets an object value for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a value is SmartThings payload handle.
+ * @remarks The @a value must be released using smartthings_payload_release_object().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] value The value associated with the given attribute name
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ */
+int smartthings_payload_get_object(smartthings_payload_h payload, const char *attr_name, smartthings_payload_h *value);
+
+/**
+ * @brief Gets an integer array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a array must be released using smartthings_payload_release_int_array().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] array The array associated with the given attribute name
+ * @param[out] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ *
+ * @see smartthings_payload_release_int_array()
+ */
+int smartthings_payload_get_int_array(smartthings_payload_h payload, const char *attr_name, int **array, unsigned int *length);
+
+/**
+ * @brief Gets a boolean array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a array must be released using smartthings_payload_release_bool_array().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] array The array associated with the given attribute name
+ * @param[out] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ *
+ * @see smartthings_payload_release_bool_array()
+ */
+int smartthings_payload_get_bool_array(smartthings_payload_h payload, const char *attr_name, bool **array, unsigned int *length);
+
+/**
+ * @brief Gets a double array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a array must be released using smartthings_payload_release_double_array().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] array The array associated with the given attribute name
+ * @param[out] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ *
+ * @see smartthings_payload_release_double_array()
+ */
+int smartthings_payload_get_double_array(smartthings_payload_h payload, const char *attr_name, double **array, unsigned int *length);
+
+/**
+ * @brief Gets a string array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a array must be released using smartthings_payload_release_string_array().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] array The array associated with the given attribute name
+ * @param[out] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ *
+ * @see smartthings_payload_release_string_array()
+ */
+int smartthings_payload_get_string_array(smartthings_payload_h payload, const char *attr_name, char ***array, unsigned int *length);
+
+/**
+ * @brief Gets an object array for the attribute name.
+ * @since_ses 1
+ *
+ * @remarks The @a attr_name is key value, one of several properties of the resource type.
+ * @remarks The @a array is a list of SmartThings payload handle.
+ * @remarks The @a array must be released using smartthings_payload_release_object_array().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] attr_name The attribute name
+ * @param[out] array The array associated with the given attribute name
+ * @param[out] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ *
+ * @see smartthings_payload_release_object_array()
+ */
+int smartthings_payload_get_object_array(smartthings_payload_h payload, const char *attr_name, smartthings_payload_h **array, unsigned int *length);
+
+/**
+ * @brief Gets a payload for child resource of collection resource.
+ * @since_ses 1
+ *
+ * @remarks The @a resource_uri is key value, one of several child resources of the collection resource.
+ * @remarks The @a value is SmartThings payload handle.
+ * @remarks The @a value must be released using smartthings_payload_release_object().
+ *
+ * @param[in] payload The SmartThings payload handle
+ * @param[in] resource_uri The resource URI of child resource
+ * @param[out] value The payload associated with the given resource URI
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NO_DATA No data
+ */
+int smartthings_payload_get_collection_object(smartthings_payload_h payload, const char *resource_uri, smartthings_payload_h *value);
+
+/**
+ * @brief Releases an integer array of the SmartThings payload.
+ * @since_ses 1
+ *
+ * @param[in] array The array to release
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_payload_get_int_array()
+ */
+int smartthings_payload_release_int_array(int *array);
+
+/**
+ * @brief Releases a boolean array of the SmartThings payload.
+ * @since_ses 1
+ *
+ * @param[in] array The array to release
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_payload_get_bool_array()
+ */
+int smartthings_payload_release_bool_array(bool *array);
+
+/**
+ * @brief Releases a double array of the SmartThings payload.
+ * @since_ses 1
+ *
+ * @param[in] array The array to release
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_payload_get_double_array()
+ */
+int smartthings_payload_release_double_array(double *array);
+
+/**
+ * @brief Releases a string array of the SmartThings payload.
+ * @since_ses 1
+ *
+ * @param[in] array The array to release
+ * @param[in] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_payload_get_string_array()
+ */
+int smartthings_payload_release_string_array(char **array, unsigned int length);
+
+/**
+ * @brief Releases an object array of the SmartThings payload.
+ * @since_ses 1
+ *
+ * @param[in] array The array to release
+ * @param[in] length The length of @a array
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_payload_get_object_array()
+ */
+int smartthings_payload_release_object_array(smartthings_payload_h *array, unsigned int length);
+
+/**
+ * @brief Releases a SmartThings payload.
+ * @since_ses 1
+ *
+ * @remarks The function is different with smartthings_payload_destroy().
+ * @remarks The function releases only payload value that is obtained by smartthings_payload_get_object().
+ *
+ * @param[in] payload The payload to release
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_payload_get_object()
+ */
+int smartthings_payload_release_object(smartthings_payload_h payload);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_PAYLOAD_H__ */
diff --git a/include/smartthings_resource.h b/include/smartthings_resource.h
new file mode 100644 (file)
index 0000000..962339b
--- /dev/null
@@ -0,0 +1,367 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_RESOURCE_H__
+#define __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_RESOURCE_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <tizen.h>
+#include <smartthings_payload.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file smartthings_resource.h
+ */
+
+/**
+ * @addtogroup CAPI_SMARTTHINGS_THING_RESOURCE_MODULE
+ * @{
+ */
+
+/**
+ * @brief Enumeration for the SmartThings resource error.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_RESOURCE_ERROR_NONE = TIZEN_ERROR_NONE,                                /**< Successful */
+       SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER,      /**< Invalid parameter */
+       SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY,              /**< Out of memory */
+       SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED,      /**< Permission denied */
+       SMARTTHINGS_RESOURCE_ERROR_NO_DATA = TIZEN_ERROR_NO_DATA,                          /**< No data */
+       SMARTTHINGS_RESOURCE_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED,                      /**< Not supported */
+       SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED = TIZEN_ERROR_UNKNOWN - 1,             /**< Operation failed */
+       SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE = TIZEN_ERROR_UNKNOWN -2            /**< Service unavailable */
+} smartthings_resource_error_e;
+
+/**
+ * @brief Enumeration for the request type of resource and collection resource.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_RESOURCE_REQUEST_GET = 0,              /**< Get request type */
+       SMARTTHINGS_RESOURCE_REQUEST_SET = 1,              /**< Set request type */
+       SMARTTHINGS_RESOURCE_REQUEST_COLLECTION_GET = 2,   /**< Get request type for collection */
+       SMARTTHINGS_RESOURCE_REQUEST_COLLECTION_SET = 3    /**< Set request type for collection */
+} smartthings_resource_req_type_e;
+
+/**
+ * @brief Enumeration for RPC connection status.
+ * @since_ses 1
+ */
+typedef enum {
+       SMARTTHINGS_RESOURCE_CONNECTION_STATUS_CONNECTED = 0,          /**< Connection is connected */
+       SMARTTHINGS_RESOURCE_CONNECTION_STATUS_DISCONNECTED,           /**< Connection is disconnected */
+       SMARTTHINGS_RESOURCE_CONNECTION_STATUS_REJECTED,               /**< Connection is rejected */
+} smartthings_resource_connection_status_e;
+
+/**
+ * @brief The SmartThings resource handle.
+ * @since_ses 1
+ */
+typedef struct smartthings_resource_s *smartthings_resource_h;
+
+/**
+ * @brief Callback for status of connection to SmartThings Thing agent.
+  * @details The following error codes can be received: \n
+       #SMARTTHINGS_RESOURCE_ERROR_NONE:                               Success \n
+       #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED:      Permission denied \n
+       #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE:   Service unavailable \n
+ * @since_ses 1
+ *
+ * @remarks The @a handle should not be released.
+ * @remarks The @a handle is the same object for which the callback was set/added.
+ * @remarks The @a handle will be released when smartthings_deinitialize() is called.
+ * @remarks When callback is called, user can see result as #smartthings_resource_error_e enumeration value.
+ * @remarks When callback is called, user can see connection status as #smartthings_connection_status_e enumeration value.
+ *
+ * @param[in] result The result of connection operation
+ * @param[in] handle The SmartThings handle
+ * @param[in] status The status of connection
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_resource_initialize()
+ */
+typedef void (*smartthings_resource_connection_status_cb)(smartthings_resource_error_e result, smartthings_resource_h handle, smartthings_resource_connection_status_e status, void *user_data);
+
+
+/**
+ * @brief Callback for handling request(GET/SET) messages.
+ * @since_ses 1
+ *
+ * @remarks The @a payload is NULL when @a req_type is #SMARTTHINGS_RESOURCE_REQUEST_GET.
+ * @remarks The @a payload can be used only in the callback. To use outside, make a copy.
+ * @remarks The @a st_h should not be released.
+ * @remarks The @a st_h is the same object for which the callback was set/added.
+ * @remarks The @a st_h will be released when smartthings_resource_deinitialize() is called.
+ * @remarks The @a payload should not be released.
+ * @remarks The @a uri can be used only in the callback. To use outside, make a copy.
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[in] req_id The request ID of request message
+ * @param[in] uri The resource URI
+ * @param[in] req_type The request type for request message
+ * @param[in] payload The payload for SET request message
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_resource_set_request_cb()
+ * @see smartthings_resource_unset_request_cb()
+ */
+typedef void (*smartthings_resource_request_cb)(smartthings_resource_h st_h, int req_id, const char *uri,
+                                                                                               smartthings_resource_req_type_e req_type,
+                                                                                               smartthings_payload_h payload, void *user_data);
+
+/**
+ * @brief Callback for status of resource registration to cloud.
+ * @since_ses 1
+ *
+ * @remarks This callback will be called when status of resource registration is changed.
+ * @remarks The @a st_h should not be released.
+ * @remarks The @a st_h is the same object for which the callback was set/added.
+ * @remarks The @a st_h will be released when smartthings_resource_deinitialize() is called.
+ * @remarks The @a is_registered will be true when resources are registered to cloud.
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[in] is_registered The status of resource registration to cloud
+ * @param[in] user_data The user data passed from the callback registration function
+ *
+ * @see smartthings_resource_set_cloud_registration_status_cb()
+ * @see smartthings_resource_unset_cloud_registration_status_cb()
+ */
+typedef void (*smartthings_resource_cloud_registration_status_cb)(smartthings_resource_h st_h, bool is_registered, void *user_data);
+
+
+/**
+ * @brief Initializes a resource handle and connects to agent.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks The @a st_h must be released using smartthings_resource_deinitialize().
+ * @remarks Ths function returns #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED\n
+ *          if the application has no app-defined privilege for 'http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource'.
+ *
+ * @param[out] st_h The SmartThings resource handle to be newly created on success
+ * @param[in] connection_status_cb The connection status callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NOT_SUPPORTED Not supported
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_resource_deinitialize()
+ */
+int smartthings_resource_initialize(smartthings_resource_h *st_h,
+                                                                  smartthings_resource_connection_status_cb connection_status_cb,
+                                                                  void *user_data);
+
+/**
+ * @brief Deinitializes a resource handle and disconnects from the agent.
+ * @since_ses 1
+ *
+ * @param[in] st_h The SmartThings resource handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ *
+ * @see smartthings_resource_initialize()
+ */
+int smartthings_resource_deinitialize(smartthings_resource_h st_h);
+
+/**
+ * @brief Sets resource request callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[in] req_cb The request callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_resource_unset_request_cb()
+ */
+int smartthings_resource_set_request_cb(smartthings_resource_h st_h,
+                                                               smartthings_resource_request_cb req_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets resource request callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] st_h The SmartThings resource handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_resource_set_request_cb()
+ */
+int smartthings_resource_unset_request_cb(smartthings_resource_h st_h);
+
+/**
+ * @brief Sends response for resource request message.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[in] req_id The request ID of request message
+ * @param[in] uri The resource URI
+ * @param[in] payload The payload of response message
+ * @param[in] result The result of response
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_resource_send_response(smartthings_resource_h st_h, int req_id, const char *uri, smartthings_payload_h payload, bool result);
+
+/**
+ * @brief Notifies resource change.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing \n
+ *            %http://tizen.org/privilege/internet
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[in] uri The resource URI
+ * @param[in] payload The payload of response message
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_resource_notify(smartthings_resource_h st_h, const char *uri, smartthings_payload_h payload);
+
+/**
+ * @brief Gets resource URIs.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @remarks The @a uris should be released using free().
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[out] count The resource count
+ * @param[out] uris The resource URI list
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ */
+int smartthings_resource_get_uris(smartthings_resource_h st_h, int *count, char ***uris);
+
+/**
+ * @brief Sets cloud registration callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] st_h The SmartThings resource handle
+ * @param[in] reg_cb The callback to register
+ * @param[in] user_data The user data to be passed to the callback function
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_resource_unset_cloud_registration_status_cb()
+ */
+int smartthings_resource_set_cloud_registration_status_cb(smartthings_resource_h st_h,
+                                                               smartthings_resource_cloud_registration_status_cb reg_cb,
+                                                               void *user_data);
+
+/**
+ * @brief Unsets cloud registration callback.
+ * @since_ses 1
+ * @privilege %http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource \n
+ *            %http://tizen.org/privilege/appmanager.launch \n
+ *            %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] st_h The SmartThings resource handle
+ *
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_NONE Successful
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED Permission denied
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED Operation failed
+ * @retval #SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE Service unavailable
+ *
+ * @see smartthings_resource_set_cloud_registration_status_cb()
+ */
+int smartthings_resource_unset_cloud_registration_status_cb(smartthings_resource_h st_h);
+
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SAMSUNG_EXPERIENCE_SERVICE_SMARTTHINGS_RESOURCE_H__ */
diff --git a/include/st_thing_master.h b/include/st_thing_master.h
new file mode 100644 (file)
index 0000000..dae1409
--- /dev/null
@@ -0,0 +1,23 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ST_THING_MASTER_H__
+#define __ST_THING_MASTER_H__
+
+int st_thing_master_init();
+int st_thing_master_fini();
+
+#endif /* __ST_THING_MASTER_H__ */
diff --git a/include/st_thing_resource.h b/include/st_thing_resource.h
new file mode 100644 (file)
index 0000000..4ebe0c1
--- /dev/null
@@ -0,0 +1,23 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ST_THING_RESOURCE__
+#define __ST_THING_RESOURCE__
+
+int st_thing_resource_init(void);
+int st_thing_resource_fini(void);
+
+#endif /* __ST_THING_RESOURCE__ */
diff --git a/include/switch.h b/include/switch.h
new file mode 100644 (file)
index 0000000..4fe682a
--- /dev/null
@@ -0,0 +1,34 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SWITCH_H__
+#define __SWITCH_H__
+
+typedef enum {
+       SWITCH_STATE_OFF,
+       SWITCH_STATE_ON
+} switch_state_e;
+
+typedef void(*switch_state_changed_cb) (switch_state_e state, void* user_data);
+
+int switch_initialize(void);
+int switch_finalize(void);
+int switch_state_set(switch_state_e state, const char *pass_key);
+int switch_state_get(switch_state_e *state);
+int switch_state_changed_cb_set(
+       const char *callback_key, switch_state_changed_cb callback, void *cb_data);
+
+#endif /* __SWITCH_H__ */
diff --git a/install-and-start.sh b/install-and-start.sh
new file mode 100755 (executable)
index 0000000..aa6c3bf
--- /dev/null
@@ -0,0 +1,11 @@
+sdb root on
+sdb shell 'mount -o remount,rw /'
+
+sdb shell 'app_launcher -t iot-vision-camera'
+
+sdb push ~/GBS-ROOT/local/BUILD-ROOTS/scratch.armv7l.0/home/abuild/rpmbuild/RPMS/armv7l/iot-vision-camera-0.0.1-1.armv7l.rpm /tmp
+sdb shell 'cd /tmp; rpm -ivh --force iot-vision-camera-0.0.1-1.armv7l.rpm'
+
+sdb shell 'app_launcher -s iot-vision-camera'
+
+sdb shell 'systemctl status iot-dashboard'
diff --git a/iot-vision-camera.manifest b/iot-vision-camera.manifest
new file mode 100644 (file)
index 0000000..ca37499
--- /dev/null
@@ -0,0 +1,6 @@
+<manifest>
+       <request>
+               <domain name="_" />
+       </request>
+</manifest>
+
diff --git a/lib/libst_thing_master_api.so b/lib/libst_thing_master_api.so
new file mode 100644 (file)
index 0000000..2e258c9
Binary files /dev/null and b/lib/libst_thing_master_api.so differ
diff --git a/lib/libst_thing_resource_api.so b/lib/libst_thing_resource_api.so
new file mode 100644 (file)
index 0000000..53b9b44
Binary files /dev/null and b/lib/libst_thing_resource_api.so differ
diff --git a/packaging/iot-dashboard.service b/packaging/iot-dashboard.service
new file mode 100644 (file)
index 0000000..ee0b41b
--- /dev/null
@@ -0,0 +1,17 @@
+[Unit]
+Description=IoT Dashboard
+After=network.target
+
+[Service]
+Type=idle
+SmackProcessLabel=System
+WorkingDirectory=/opt/home/dashboard
+ExecStart=/usr/bin/iotjs server.js
+Restart=on-failure
+RestartSec=0
+NonBlocking=yes
+KillMode=process
+TimeoutStopSec=5
+
+[Install]
+WantedBy=multi-user.target
diff --git a/packaging/iot-vision-camera.spec b/packaging/iot-vision-camera.spec
new file mode 100644 (file)
index 0000000..c6ecdf9
--- /dev/null
@@ -0,0 +1,96 @@
+Name:       iot-vision-camera
+%define dashboard iot-dashboard
+Summary:    IoT Vision Camera App
+Group:      Applications/Core Applications
+Version:    0.0.1
+Release:    1
+License:    Apache-2.0
+Provides:   %{name} = %{version}-%{release}
+Source0:    %{name}-%{version}.tar.gz
+Source1:    %{dashboard}.service
+
+BuildRequires: cmake
+BuildRequires: hash-signer
+BuildRequires: pkgconfig(ecore)
+BuildRequires: pkgconfig(capi-appfw-service-application)
+BuildRequires: pkgconfig(capi-system-peripheral-io)
+BuildRequires: pkgconfig(libtzplatform-config)
+BuildRequires: pkgconfig(gio-2.0)
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(capi-media-camera)
+BuildRequires: pkgconfig(capi-media-vision)
+BuildRequires: pkgconfig(capi-media-image-util)
+BuildRequires: pkgconfig(rpc-port)
+BuildRequires: pkgconfig(libexif)
+BuildRequires: pkgconfig(capi-appfw-package-manager)
+
+%description
+IoT Vision Camera Application
+
+%prep
+%setup -q
+
+%build
+
+%ifarch %{arm}
+export CFLAGS="$CFLAGS -DTIZEN_BUILD_TARGET"
+export CXXFLAGS="$CXXFLAGS -DTIZEN_BUILD_TARGET"
+export FFLAGS="$FFLAGS -DTIZEN_BUILD_TARGET"
+%else
+export CFLAGS="$CFLAGS -DTIZEN_BUILD_EMULATOR"
+export CXXFLAGS="$CXXFLAGS -DTIZEN_BUILD_EMULATOR"
+export FFLAGS="$FFLAGS -DTIZEN_BUILD_EMULATOR"
+%endif
+
+%define _pkg_dir %{TZ_SYS_RO_APP}/%{name}
+%define _pkg_shared_dir %{_pkg_dir}/shared
+%define _pkg_res_dir %{_pkg_dir}/res
+%define _pkg_shared_res_dir %{_pkg_shared_dir}/res
+%define _sys_packages_dir %{TZ_SYS_RO_PACKAGES}
+%define _dashboard_dir /opt/home/
+
+cmake . -DP_NAME=%{name} \
+       -DINSTALL_PREFIX=%{_pkg_dir} \
+       -DAPP_SHARED_RES_DIR=%{_pkg_shared_res_dir} \
+       -DDASH_BOARD_DIR=%{_dashboard_dir} \
+       -DSYS_PACKAGES_DIR=%{_sys_packages_dir} \
+
+make %{?jobs:-j%jobs}
+
+%install
+%make_install
+
+mkdir -p %{buildroot}%{_unitdir}/multi-user.target.wants
+install -m 0644 %SOURCE1 %{buildroot}%{_unitdir}/%{dashboard}.service
+%install_service multi-user.target.wants %{dashboard}.service
+
+%define tizen_sign 1
+%define tizen_sign_base %{_pkg_dir}
+%define tizen_sign_level platform
+%define tizen_author_sign 1
+%define tizen_dist_sign 1
+
+%post
+/bin/systemctl stop %{dashboard}
+/bin/systemctl daemon-reload
+/bin/systemctl start %{dashboard}
+
+%postun
+/bin/systemctl stop %{dashboard}
+
+%files
+%manifest %{name}.manifest
+%license LICENSE NOTICE
+%defattr(-,root,root,-)
+%{_pkg_dir}/bin/%{name}
+%{_pkg_dir}/lib/*
+%{_pkg_dir}/author-signature.xml
+%{_pkg_dir}/signature1.xml
+%{_sys_packages_dir}/%{name}.xml
+%{_pkg_shared_res_dir}/*.png
+%{_pkg_res_dir}/*
+%{_pkg_shared_res_dir}/*.json
+%{_unitdir}/%{dashboard}.service
+%{_unitdir}/multi-user.target.wants/%{dashboard}.service
+%{_dashboard_dir}/*
diff --git a/shared/res/default_icon.png b/shared/res/default_icon.png
new file mode 100644 (file)
index 0000000..9765b1b
Binary files /dev/null and b/shared/res/default_icon.png differ
diff --git a/shared/res/master.json b/shared/res/master.json
new file mode 100644 (file)
index 0000000..801f2a7
--- /dev/null
@@ -0,0 +1,21 @@
+{
+       "devices": [
+               {
+                       "deviceType": "oic.d.camera",
+                       "deviceName": "YOUR-DEVICE-NAME",
+                       "mnid": "YOUR-MNID",
+                       "vid": "YOUR-VENDER-ID"
+               }
+       ],
+       "configuration": {
+               "otm": 2,
+               "otmDescription" : "ownershipTransferMethod : b0001(Random PIN-based), b0010(UserConfirm-based)",
+               "crtType" : 1,
+               "crtDescription" : "certificate type: 1(Test Certificate), 2(Artik Certificate)",
+               "easySetup": {
+                       "mode": 1,
+                       "modeDescription" : "easysetup mode : 1(SoftAP)",
+                       "setupId": "YOUR-SETUP-ID"
+               }
+       }
+}
diff --git a/shared/res/resource.json b/shared/res/resource.json
new file mode 100644 (file)
index 0000000..ead0010
--- /dev/null
@@ -0,0 +1,105 @@
+{
+       "resources": {
+               "single": [
+                       {
+                               "uri": "/capability/switch/main/0",
+                               "types": [
+                                       "x.com.st.powerswitch"
+                               ],
+                               "interfaces": [
+                                       "oic.if.a",
+                                       "oic.if.baseline"
+                               ]
+                       },
+                       {
+                               "uri": "/capability/motionSensor/main/0",
+                               "types": [
+                                 "oic.r.sensor.motion"
+                               ],
+                               "interfaces": [
+                                 "oic.if.s",
+                                 "oic.if.baseline"
+                               ]
+                       },
+                       {
+                               "uri": "/capability/switchLevel/main/0",
+                               "types": [
+                                 "oic.r.light.dimming"
+                               ],
+                               "interfaces": [
+                                 "oic.if.a",
+                                 "oic.if.baseline"
+                               ]
+                       },
+                       {
+                               "uri": "/capability/audioVolume/main/0",
+                               "types": [
+                                       "x.com.st.audiovolume"
+                               ],
+                               "interfaces": [
+                                       "oic.if.a",
+                                       "oic.if.baseline"
+                               ]
+                       }
+               ]
+       },
+       "resourceTypes": [
+               {
+                       "type": "x.com.st.powerswitch",
+                       "properties": [
+                               {
+                                       "key": "power",
+                                       "type": "string",
+                                       "mandatory": true,
+                                       "description": "State of the power switch.",
+                                       "enumDescription": ["on", "off"]
+                               }
+                       ]
+               },
+               {
+                       "type": "oic.r.sensor.motion",
+                       "properties": [
+                               {
+                                       "key": "value",
+                                       "type": "boolean",
+                                       "mandatory": true,
+                                       "description": "motion detect"
+                               }
+                       ]
+               },
+               {
+                       "type": "oic.r.light.dimming",
+                       "properties": [
+                               {
+                               "key": "dimmingSetting",
+                               "type": "integer",
+                               "mandatory": true,
+                               "description": "test dimming value."
+                               }
+                       ]
+               },
+               {
+                       "type": "x.com.st.audiovolume",
+                       "properties": [
+                               {
+                                       "key": "volume",
+                                       "type": "integer",
+                                       "mandatory": true,
+                                       "description": "Volume setting of an audio rendering device."
+                               },
+                               {
+                                       "key": "mute",
+                                       "type": "boolean",
+                                       "mandatory": true,
+                                       "description": "Mute setting of an audio rendering device"
+                               },
+                               {
+                                       "key": "command",
+                                       "type": "string",
+                                       "description": "Comand for controlling",
+                                       "enumDescription": ["increase", "decrease", "max", "min"]
+                               }
+                       ]
+               }
+       ]
+}
diff --git a/smartthings-plugin/device-profile.json b/smartthings-plugin/device-profile.json
new file mode 100644 (file)
index 0000000..91ef59e
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "name": "YOUR_DEVICE_NAME",
+  "components": [
+    {
+      "id": "main",
+      "capabilities": [
+        {
+          "id": "switch"
+        },
+        {
+          "id": "audioVolume"
+        },
+        {
+          "id": "motionSensor"
+        },
+        {
+          "id": "switchLevel"
+        }
+      ]
+    }
+  ],
+  "metadata": {
+    "mnmn": "",
+    "vid": "YOUR_VENDER_ID",
+    "deviceType": "Others"
+  }
+}
\ No newline at end of file
diff --git a/smartthings-plugin/manifest/ui.json b/smartthings-plugin/manifest/ui.json
new file mode 100644 (file)
index 0000000..cc3cfad
--- /dev/null
@@ -0,0 +1,594 @@
+{
+  "n": "YOUR_DEVICE_NAME",
+  "version": "0.0.1",
+  "vid": "",
+  "mnmn": "",
+  "language": [
+    {
+      "locale": "en_US",
+      "version": "0.0.1",
+      "poCodes": [
+        {
+          "po": "___PO_CODE_POWERSWITCH_ON",
+          "label": "On"
+        },
+        {
+          "po": "___PO_CODE_POWERSWITCH_OFF",
+          "label": "Off"
+        },
+        {
+          "po": "___PO_CODE_POWERSWITCH",
+          "label": "Power"
+        },
+        {
+          "label": "Command",
+          "po": "___PO_CODE_AUDIOVOLUME_COMMAND"
+        },
+        {
+          "label": "Increase",
+          "po": "___PO_CODE_AUDIOVOLUME_INCREASE"
+        },
+        {
+          "label": "Decrease",
+          "po": "___PO_CODE_AUDIOVOLUME_DECREASE"
+        },
+        {
+          "label": "Max",
+          "po": "___PO_CODE_AUDIOVOLUME_MAX"
+        },
+        {
+          "label": "Min",
+          "po": "___PO_CODE_AUDIOVOLUME_MIN"
+        },
+        {
+          "label": "Volume",
+          "po": "___PO_CODE_AUDIOVOLUME_VOLUME"
+        },
+        {
+          "label": "Mute",
+          "po": "___PO_CODE_AUDIOVOLUME_MUTE"
+        },
+        {
+          "label": "Unmute",
+          "po": "___PO_CODE_AUDIOVOLUME_UNMUTE"
+        },
+        {
+          "label": "Volume",
+          "po": "___PO_CODE_AUDIOVOLUME_MUTE_ACTION"
+        },
+        {
+          "label": "Motion sensor",
+          "po": "___PO_CODE_MOTIONSENSOR"
+        },
+        {
+          "label": "No motion",
+          "po": "___PO_CODE_MOTIONSENSOR_NO_MOTION"
+        },
+        {
+          "label": "Motion detected",
+          "po": "___PO_CODE_MOTIONSENSOR_MOTION_DETECTED"
+        },
+        {
+          "label": "Dimmer",
+          "po": "___PO_CODE_SWITCHLEVEL_DIMMING"
+        }
+      ],
+      "mnmn": "",
+      "vid": ""
+    }
+  ],
+  "ma": [
+    {
+      "name": "PowerSwitch",
+      "type": "main",
+      "order": 1,
+      "link": {
+        "href": "/capability/switch/main/0",
+        "if": "oic.if.a",
+        "rt": "x.com.st.powerswitch"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_power_switch"
+      },
+      "property": "power",
+      "controlType": "ToggleSwitch",
+      "alternatives": [
+        {
+          "key": "on",
+          "value": "___PO_CODE_POWERSWITCH_ON"
+        },
+        {
+          "key": "off",
+          "value": "___PO_CODE_POWERSWITCH_OFF",
+          "type": "inactive"
+        }
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_POWERSWITCH"
+      },
+      "emphasis": false
+    }
+  ],
+  "ms": [
+    {
+      "name": "PowerSwitch",
+      "type": "device",
+      "order": 1,
+      "label": {
+        "label": "<var1>",
+        "arguments": [
+          {
+            "n": "var1",
+            "href": "/capability/switch/main/0",
+            "property": "power",
+            "valueType": null,
+            "alternatives": [
+              {
+                "key": "on",
+                "value": "___PO_CODE_POWERSWITCH_ON"
+              },
+              {
+                "key": "off",
+                "value": "___PO_CODE_POWERSWITCH_OFF",
+                "type": "inactive"
+              }
+            ]
+          }
+        ]
+      },
+      "emphasis": false
+    }
+  ],
+  "dpInfo": [
+    {
+      "os": "android",
+      "dpType": "vendorPlugIn",
+      "dpUri": "wwst://iot2018"
+    },
+    {
+      "os": "ios",
+      "dpType": "vendorPlugIn",
+      "dpUri": "wwst://iot2018"
+    }
+  ],
+  "dpResources": [
+    {
+      "name": "PowerSwitch",
+      "controllable": true,
+      "order": 1,
+      "link": {
+        "href": "/capability/switch/main/0",
+        "if": "oic.if.a",
+        "rt": "x.com.st.powerswitch"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_power_switch"
+      },
+      "property": "power",
+      "controlType": "ToggleSwitch",
+      "alternatives": [
+        {
+          "key": "on",
+          "value": "___PO_CODE_POWERSWITCH_ON"
+        },
+        {
+          "key": "off",
+          "value": "___PO_CODE_POWERSWITCH_OFF",
+          "type": "inactive"
+        }
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_POWERSWITCH"
+      },
+      "emphasis": true,
+      "ruleEvent": false,
+      "ruleAction": true
+    },
+    {
+      "name": "PowerSwitch",
+      "controllable": true,
+      "order": 2,
+      "link": {
+        "href": "/capability/switch/main/0",
+        "if": "oic.if.a",
+        "rt": "x.com.st.powerswitch"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_power_switch"
+      },
+      "property": "power",
+      "controlType": "ToggleSwitch",
+      "alternatives": [
+        {
+          "key": "on",
+          "value": "___PO_CODE_POWERSWITCH_ON"
+        },
+        {
+          "key": "off",
+          "value": "___PO_CODE_POWERSWITCH_OFF",
+          "type": "inactive"
+        }
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_POWERSWITCH"
+      },
+      "emphasis": true,
+      "ruleEvent": false,
+      "ruleAction": false
+    },
+    {
+      "name": "PowerSwitch",
+      "controllable": true,
+      "order": 3,
+      "link": {
+        "href": "/capability/switch/main/0",
+        "if": "oic.if.a",
+        "rt": "x.com.st.powerswitch"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_power_switch"
+      },
+      "property": "power",
+      "controlType": "ToggleSwitch",
+      "alternatives": [
+        {
+          "key": "on",
+          "value": "___PO_CODE_POWERSWITCH_ON"
+        },
+        {
+          "key": "off",
+          "value": "___PO_CODE_POWERSWITCH_OFF",
+          "type": "inactive"
+        }
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_POWERSWITCH"
+      },
+      "emphasis": true,
+      "ruleEvent": true,
+      "ruleAction": false
+    },
+    {
+      "controlType": "List",
+      "name": "Audio Volume Command",
+      "controllable": true,
+      "property": "command",
+      "emphasis": false,
+      "link": {
+        "rt": "x.com.st.audiovolume",
+        "href": "/capability/audioVolume/main/0",
+        "if": "oic.if.a"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_audio_volume"
+      },
+      "alternatives": [
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_INCREASE",
+          "key": "increase"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_DECREASE",
+          "key": "decrease"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_MAX",
+          "key": "max"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_MIN",
+          "key": "min"
+        }
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_AUDIOVOLUME_COMMAND",
+        "n": "Audio Volume Command"
+      },
+      "ruleEvent": false,
+      "ruleAction": false,
+      "order": 4
+    },
+    {
+      "controlType": "List",
+      "name": "Audio Volume Command",
+      "controllable": true,
+      "property": "command",
+      "emphasis": false,
+      "link": {
+        "rt": "x.com.st.audiovolume",
+        "href": "/capability/audioVolume/main/0",
+        "if": "oic.if.a"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_audio_volume"
+      },
+      "alternatives": [
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_INCREASE",
+          "key": "increase"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_DECREASE",
+          "key": "decrease"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_MAX",
+          "key": "max"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_MIN",
+          "key": "min"
+        }
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_AUDIOVOLUME_COMMAND",
+        "n": "Audio Volume Command"
+      },
+      "ruleEvent": false,
+      "ruleAction": true,
+      "order": 5
+    },
+    {
+      "controlType": "List",
+      "name": "Audio Volume Command",
+      "controllable": true,
+      "property": "command",
+      "emphasis": false,
+      "link": {
+        "rt": "x.com.st.audiovolume",
+        "href": "/capability/audioVolume/main/0",
+        "if": "oic.if.a"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_audio_volume"
+      },
+      "alternatives": [
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_INCREASE",
+          "key": "increase"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_DECREASE",
+          "key": "decrease"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_MAX",
+          "key": "max"
+        },
+        {
+          "value": "___PO_CODE_AUDIOVOLUME_MIN",
+          "key": "min"
+        }
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_AUDIOVOLUME_COMMAND",
+        "n": "Audio Volume Command"
+      },
+      "ruleEvent": true,
+      "ruleAction": false,
+      "order": 6
+    },
+    {
+      "controlType": "slider",
+      "name": "Audio Volume",
+      "controllable": true,
+      "property": "volume",
+      "emphasis": false,
+      "link": {
+        "rt": "x.com.st.audiovolume",
+        "href": "/capability/audioVolume/main/0",
+        "if": "oic.if.a"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_audio_volume"
+      },
+      "range": [
+        0,
+        100
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_AUDIOVOLUME_VOLUME",
+        "n": "Volume"
+      },
+      "ruleEvent": false,
+      "ruleAction": false,
+      "order": 7
+    },
+    {
+      "controlType": "slider",
+      "name": "Audio Volume",
+      "controllable": true,
+      "property": "volume",
+      "emphasis": false,
+      "link": {
+        "rt": "x.com.st.audiovolume",
+        "href": "/capability/audioVolume/main/0",
+        "if": "oic.if.a"
+      },
+      "iconUrl": {
+        "vector": "",
+        "small": "",
+        "large": "",
+        "mid": "icon://ic_function_audio_volume"
+      },
+      "range": [
+        0,
+        100
+      ],
+      "step": 0,
+      "label": {
+        "label": "___PO_CODE_AUDIOVOLUME_VOLUME",
+        "n": "Volume"
+      },
+      "ruleEvent": false,
+      "ruleAction": true,
+      "order": 8
+    },
+    {
+      "ruleAction": false,
+      "emphasis": false,
+      "controllable": false,
+      "step": 0,
+      "link": {
+        "rt": "oic.r.sensor.motion",
+        "href": "/capability/motionSensor/main/0",
+        "if": "oic.if.r"
+      },
+      "controlType": "List",
+      "name": "Motion Sensor",
+      "label": {
+        "label": "___PO_CODE_MOTIONSENSOR",
+        "n": "ST Motion Sensor resource"
+      },
+      "iconUrl": {
+        "mid": "icon://ic_function_motion_sensor",
+        "large": "",
+        "small": "",
+        "vector": ""
+      },
+      "ruleEvent": false,
+      "alternatives": [
+        {
+          "type": "inactive",
+          "value": "___PO_CODE_MOTIONSENSOR_NO_MOTION",
+          "key": "false"
+        },
+        {
+          "value": "___PO_CODE_MOTIONSENSOR_MOTION_DETECTED",
+          "key": "true"
+        }
+      ],
+      "order": 9,
+      "property": "value"
+    },
+    {
+      "ruleAction": false,
+      "emphasis": false,
+      "controllable": false,
+      "step": 0,
+      "link": {
+        "rt": "oic.r.sensor.motion",
+        "href": "/capability/motionSensor/main/0",
+        "if": "oic.if.r"
+      },
+      "controlType": "List",
+      "name": "Motion Sensor",
+      "label": {
+        "label": "___PO_CODE_MOTIONSENSOR",
+        "n": "ST Motion Sensor resource"
+      },
+      "iconUrl": {
+        "mid": "icon://ic_function_motion_sensor",
+        "large": "",
+        "small": "",
+        "vector": ""
+      },
+      "ruleEvent": true,
+      "alternatives": [
+        {
+          "type": "inactive",
+          "value": "___PO_CODE_MOTIONSENSOR_NO_MOTION",
+          "key": "false"
+        },
+        {
+          "value": "___PO_CODE_MOTIONSENSOR_MOTION_DETECTED",
+          "key": "true"
+        }
+      ],
+      "order": 10,
+      "property": "value"
+    },
+    {
+      "ruleAction": true,
+      "controllable": true,
+      "range": [
+        1,
+        100
+      ],
+      "link": {
+        "rt": "oic.r.light.dimming",
+        "href": "/capability/switchLevel/main/0",
+        "if": "oic.if.a"
+      },
+      "step": 0,
+      "controlType": "List",
+      "name": "Switch Level",
+      "label": {
+        "label": "___PO_CODE_SWITCHLEVEL_DIMMING",
+        "n": "st dimming level resource"
+      },
+      "emphasis": false,
+      "ruleEvent": false,
+      "iconUrl": {
+        "mid": "icon://ic_function_light_dimmer",
+        "large": "",
+        "small": "",
+        "vector": ""
+      },
+      "order": 11,
+      "property": "dimmingSetting"
+    },
+    {
+      "ruleAction": false,
+      "controllable": true,
+      "range": [
+        1,
+        100
+      ],
+      "link": {
+        "rt": "oic.r.light.dimming",
+        "href": "/capability/switchLevel/main/0",
+        "if": "oic.if.a"
+      },
+      "step": 0,
+      "controlType": "List",
+      "name": "Switch Level",
+      "label": {
+        "label": "___PO_CODE_SWITCHLEVEL_DIMMING",
+        "n": "st dimming level resource"
+      },
+      "emphasis": false,
+      "ruleEvent": false,
+      "iconUrl": {
+        "mid": "icon://ic_function_light_dimmer",
+        "large": "",
+        "small": "",
+        "vector": ""
+      },
+      "order": 12,
+      "property": "dimmingSetting"
+    }
+  ]
+}
diff --git a/smartthings-plugin/plugin/css/bulma-switch.min.css b/smartthings-plugin/plugin/css/bulma-switch.min.css
new file mode 100644 (file)
index 0000000..c7d659e
--- /dev/null
@@ -0,0 +1 @@
+@-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.switch[type=checkbox]{outline:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:inline-block;position:absolute;opacity:0}.switch[type=checkbox][disabled]{cursor:not-allowed}.switch[type=checkbox][disabled]+label{opacity:.5}.switch[type=checkbox][disabled]+label::before{opacity:.5}.switch[type=checkbox][disabled]+label::after{opacity:.5}.switch[type=checkbox][disabled]+label:hover{cursor:not-allowed}.switch[type=checkbox]+label{position:relative;display:initial;font-size:1rem;line-height:initial;padding-left:3.5rem;padding-top:.2rem;cursor:pointer}.switch[type=checkbox]+label::before{position:absolute;display:block;top:0;left:0;width:3rem;height:1.5rem;border:.1rem solid transparent;border-radius:3px;background:#b5b5b5;content:''}.switch[type=checkbox]+label::after{display:block;position:absolute;top:.25rem;left:.25rem;width:1rem;height:1rem;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);border-radius:3px;background:#fff;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;content:''}.switch[type=checkbox].is-rtl+label{padding-left:0;padding-right:3.5rem}.switch[type=checkbox].is-rtl+label::before{left:auto;right:0}.switch[type=checkbox].is-rtl+label::after{left:auto;right:.25rem}.switch[type=checkbox]:checked+label::before{background:#00d1b2}.switch[type=checkbox]:checked+label::after{left:1.625rem}.switch[type=checkbox]:checked.is-rtl+label::after{left:auto;right:1.625rem}.switch[type=checkbox].is-outlined+label::before{background-color:transparent;border-color:#b5b5b5}.switch[type=checkbox].is-outlined+label::after{background:#b5b5b5}.switch[type=checkbox].is-outlined:checked+label::before{background-color:transparent;border-color:#00d1b2}.switch[type=checkbox].is-outlined:checked+label::after{background:#00d1b2}.switch[type=checkbox].is-thin+label::before{top:.54545rem;height:.375rem}.switch[type=checkbox].is-thin+label::after{-webkit-box-shadow:0 0 3px #7a7a7a;box-shadow:0 0 3px #7a7a7a}.switch[type=checkbox].is-rounded+label::before{border-radius:20px}.switch[type=checkbox].is-rounded+label::after{border-radius:50%}.switch[type=checkbox].is-small+label{position:relative;display:initial;font-size:.75rem;line-height:initial;padding-left:2.75rem;padding-top:.2rem;cursor:pointer}.switch[type=checkbox].is-small+label::before{position:absolute;display:block;top:0;left:0;width:2.25rem;height:1.125rem;border:.1rem solid transparent;border-radius:3px;background:#b5b5b5;content:''}.switch[type=checkbox].is-small+label::after{display:block;position:absolute;top:.25rem;left:.25rem;width:.625rem;height:.625rem;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);border-radius:3px;background:#fff;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;content:''}.switch[type=checkbox].is-small.is-rtl+label{padding-left:0;padding-right:2.75rem}.switch[type=checkbox].is-small.is-rtl+label::before{left:auto;right:0}.switch[type=checkbox].is-small.is-rtl+label::after{left:auto;right:.25rem}.switch[type=checkbox].is-small:checked+label::before{background:#00d1b2}.switch[type=checkbox].is-small:checked+label::after{left:1.25rem}.switch[type=checkbox].is-small:checked.is-rtl+label::after{left:auto;right:1.25rem}.switch[type=checkbox].is-small.is-outlined+label::before{background-color:transparent;border-color:#b5b5b5}.switch[type=checkbox].is-small.is-outlined+label::after{background:#b5b5b5}.switch[type=checkbox].is-small.is-outlined:checked+label::before{background-color:transparent;border-color:#00d1b2}.switch[type=checkbox].is-small.is-outlined:checked+label::after{background:#00d1b2}.switch[type=checkbox].is-small.is-thin+label::before{top:.40909rem;height:.28125rem}.switch[type=checkbox].is-small.is-thin+label::after{-webkit-box-shadow:0 0 3px #7a7a7a;box-shadow:0 0 3px #7a7a7a}.switch[type=checkbox].is-small.is-rounded+label::before{border-radius:20px}.switch[type=checkbox].is-small.is-rounded+label::after{border-radius:50%}.switch[type=checkbox].is-medium+label{position:relative;display:initial;font-size:1.25rem;line-height:initial;padding-left:4.25rem;padding-top:.2rem;cursor:pointer}.switch[type=checkbox].is-medium+label::before{position:absolute;display:block;top:0;left:0;width:3.75rem;height:1.875rem;border:.1rem solid transparent;border-radius:3px;background:#b5b5b5;content:''}.switch[type=checkbox].is-medium+label::after{display:block;position:absolute;top:.25rem;left:.25rem;width:1.375rem;height:1.375rem;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);border-radius:3px;background:#fff;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;content:''}.switch[type=checkbox].is-medium.is-rtl+label{padding-left:0;padding-right:4.25rem}.switch[type=checkbox].is-medium.is-rtl+label::before{left:auto;right:0}.switch[type=checkbox].is-medium.is-rtl+label::after{left:auto;right:.25rem}.switch[type=checkbox].is-medium:checked+label::before{background:#00d1b2}.switch[type=checkbox].is-medium:checked+label::after{left:2rem}.switch[type=checkbox].is-medium:checked.is-rtl+label::after{left:auto;right:2rem}.switch[type=checkbox].is-medium.is-outlined+label::before{background-color:transparent;border-color:#b5b5b5}.switch[type=checkbox].is-medium.is-outlined+label::after{background:#b5b5b5}.switch[type=checkbox].is-medium.is-outlined:checked+label::before{background-color:transparent;border-color:#00d1b2}.switch[type=checkbox].is-medium.is-outlined:checked+label::after{background:#00d1b2}.switch[type=checkbox].is-medium.is-thin+label::before{top:.68182rem;height:.46875rem}.switch[type=checkbox].is-medium.is-thin+label::after{-webkit-box-shadow:0 0 3px #7a7a7a;box-shadow:0 0 3px #7a7a7a}.switch[type=checkbox].is-medium.is-rounded+label::before{border-radius:20px}.switch[type=checkbox].is-medium.is-rounded+label::after{border-radius:50%}.switch[type=checkbox].is-large+label{position:relative;display:initial;font-size:1.5rem;line-height:initial;padding-left:5rem;padding-top:.2rem;cursor:pointer}.switch[type=checkbox].is-large+label::before{position:absolute;display:block;top:0;left:0;width:4.5rem;height:2.25rem;border:.1rem solid transparent;border-radius:3px;background:#b5b5b5;content:''}.switch[type=checkbox].is-large+label::after{display:block;position:absolute;top:.25rem;left:.25rem;width:1.75rem;height:1.75rem;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);border-radius:3px;background:#fff;-webkit-transition:all .25s ease-out;transition:all .25s ease-out;content:''}.switch[type=checkbox].is-large.is-rtl+label{padding-left:0;padding-right:5rem}.switch[type=checkbox].is-large.is-rtl+label::before{left:auto;right:0}.switch[type=checkbox].is-large.is-rtl+label::after{left:auto;right:.25rem}.switch[type=checkbox].is-large:checked+label::before{background:#00d1b2}.switch[type=checkbox].is-large:checked+label::after{left:2.375rem}.switch[type=checkbox].is-large:checked.is-rtl+label::after{left:auto;right:2.375rem}.switch[type=checkbox].is-large.is-outlined+label::before{background-color:transparent;border-color:#b5b5b5}.switch[type=checkbox].is-large.is-outlined+label::after{background:#b5b5b5}.switch[type=checkbox].is-large.is-outlined:checked+label::before{background-color:transparent;border-color:#00d1b2}.switch[type=checkbox].is-large.is-outlined:checked+label::after{background:#00d1b2}.switch[type=checkbox].is-large.is-thin+label::before{top:.81818rem;height:.5625rem}.switch[type=checkbox].is-large.is-thin+label::after{-webkit-box-shadow:0 0 3px #7a7a7a;box-shadow:0 0 3px #7a7a7a}.switch[type=checkbox].is-large.is-rounded+label::before{border-radius:20px}.switch[type=checkbox].is-large.is-rounded+label::after{border-radius:50%}.switch[type=checkbox].is-white:checked+label::before{background:#fff}.switch[type=checkbox].is-white.is-outlined:checked+label::before{background-color:transparent;border-color:#fff!important}.switch[type=checkbox].is-white.is-outlined:checked+label::after{background:#fff}.switch[type=checkbox].is-white.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-white+label::before{background:#fff}.switch[type=checkbox].is-unchecked-white.is-outlined+label::before{background-color:transparent;border-color:#fff!important}.switch[type=checkbox].is-unchecked-white.is-outlined+label::after{background:#fff}.switch[type=checkbox].is-black:checked+label::before{background:#0a0a0a}.switch[type=checkbox].is-black.is-outlined:checked+label::before{background-color:transparent;border-color:#0a0a0a!important}.switch[type=checkbox].is-black.is-outlined:checked+label::after{background:#0a0a0a}.switch[type=checkbox].is-black.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-black+label::before{background:#0a0a0a}.switch[type=checkbox].is-unchecked-black.is-outlined+label::before{background-color:transparent;border-color:#0a0a0a!important}.switch[type=checkbox].is-unchecked-black.is-outlined+label::after{background:#0a0a0a}.switch[type=checkbox].is-light:checked+label::before{background:#f5f5f5}.switch[type=checkbox].is-light.is-outlined:checked+label::before{background-color:transparent;border-color:#f5f5f5!important}.switch[type=checkbox].is-light.is-outlined:checked+label::after{background:#f5f5f5}.switch[type=checkbox].is-light.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-light+label::before{background:#f5f5f5}.switch[type=checkbox].is-unchecked-light.is-outlined+label::before{background-color:transparent;border-color:#f5f5f5!important}.switch[type=checkbox].is-unchecked-light.is-outlined+label::after{background:#f5f5f5}.switch[type=checkbox].is-dark:checked+label::before{background:#363636}.switch[type=checkbox].is-dark.is-outlined:checked+label::before{background-color:transparent;border-color:#363636!important}.switch[type=checkbox].is-dark.is-outlined:checked+label::after{background:#363636}.switch[type=checkbox].is-dark.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-dark+label::before{background:#363636}.switch[type=checkbox].is-unchecked-dark.is-outlined+label::before{background-color:transparent;border-color:#363636!important}.switch[type=checkbox].is-unchecked-dark.is-outlined+label::after{background:#363636}.switch[type=checkbox].is-primary:checked+label::before{background:#00d1b2}.switch[type=checkbox].is-primary.is-outlined:checked+label::before{background-color:transparent;border-color:#00d1b2!important}.switch[type=checkbox].is-primary.is-outlined:checked+label::after{background:#00d1b2}.switch[type=checkbox].is-primary.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-primary+label::before{background:#00d1b2}.switch[type=checkbox].is-unchecked-primary.is-outlined+label::before{background-color:transparent;border-color:#00d1b2!important}.switch[type=checkbox].is-unchecked-primary.is-outlined+label::after{background:#00d1b2}.switch[type=checkbox].is-link:checked+label::before{background:#3273dc}.switch[type=checkbox].is-link.is-outlined:checked+label::before{background-color:transparent;border-color:#3273dc!important}.switch[type=checkbox].is-link.is-outlined:checked+label::after{background:#3273dc}.switch[type=checkbox].is-link.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-link+label::before{background:#3273dc}.switch[type=checkbox].is-unchecked-link.is-outlined+label::before{background-color:transparent;border-color:#3273dc!important}.switch[type=checkbox].is-unchecked-link.is-outlined+label::after{background:#3273dc}.switch[type=checkbox].is-info:checked+label::before{background:#209cee}.switch[type=checkbox].is-info.is-outlined:checked+label::before{background-color:transparent;border-color:#209cee!important}.switch[type=checkbox].is-info.is-outlined:checked+label::after{background:#209cee}.switch[type=checkbox].is-info.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-info+label::before{background:#209cee}.switch[type=checkbox].is-unchecked-info.is-outlined+label::before{background-color:transparent;border-color:#209cee!important}.switch[type=checkbox].is-unchecked-info.is-outlined+label::after{background:#209cee}.switch[type=checkbox].is-success:checked+label::before{background:#23d160}.switch[type=checkbox].is-success.is-outlined:checked+label::before{background-color:transparent;border-color:#23d160!important}.switch[type=checkbox].is-success.is-outlined:checked+label::after{background:#23d160}.switch[type=checkbox].is-success.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-success+label::before{background:#23d160}.switch[type=checkbox].is-unchecked-success.is-outlined+label::before{background-color:transparent;border-color:#23d160!important}.switch[type=checkbox].is-unchecked-success.is-outlined+label::after{background:#23d160}.switch[type=checkbox].is-warning:checked+label::before{background:#ffdd57}.switch[type=checkbox].is-warning.is-outlined:checked+label::before{background-color:transparent;border-color:#ffdd57!important}.switch[type=checkbox].is-warning.is-outlined:checked+label::after{background:#ffdd57}.switch[type=checkbox].is-warning.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-warning+label::before{background:#ffdd57}.switch[type=checkbox].is-unchecked-warning.is-outlined+label::before{background-color:transparent;border-color:#ffdd57!important}.switch[type=checkbox].is-unchecked-warning.is-outlined+label::after{background:#ffdd57}.switch[type=checkbox].is-danger:checked+label::before{background:#ff3860}.switch[type=checkbox].is-danger.is-outlined:checked+label::before{background-color:transparent;border-color:#ff3860!important}.switch[type=checkbox].is-danger.is-outlined:checked+label::after{background:#ff3860}.switch[type=checkbox].is-danger.is-thin.is-outlined+label::after{-webkit-box-shadow:none;box-shadow:none}.switch[type=checkbox].is-unchecked-danger+label::before{background:#ff3860}.switch[type=checkbox].is-unchecked-danger.is-outlined+label::before{background-color:transparent;border-color:#ff3860!important}.switch[type=checkbox].is-unchecked-danger.is-outlined+label::after{background:#ff3860}
\ No newline at end of file
diff --git a/smartthings-plugin/plugin/css/bulma.min.css b/smartthings-plugin/plugin/css/bulma.min.css
new file mode 100644 (file)
index 0000000..935ab34
--- /dev/null
@@ -0,0 +1,2 @@
+/*! bulma.io v0.6.2 | MIT License | github.com/jgthms/bulma */@-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}/*! minireset.css v0.0.2 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{-webkit-box-sizing:border-box;box-sizing:border-box}*{-webkit-box-sizing:inherit;box-sizing:inherit}:after,:before{-webkit-box-sizing:inherit;box-sizing:inherit}audio,embed,img,object,video{max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0;text-align:left}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,select,textarea{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1rem;font-weight:400;line-height:1.5}a{color:#3273dc;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{background-color:#f5f5f5;color:#ff3860;font-size:.875em;font-weight:400;padding:.25em .5em .25em}hr{background-color:#dbdbdb;border:none;display:block;height:1px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{text-align:left;vertical-align:top}table th{color:#363636}.is-clearfix:after{clear:both;content:" ";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-clipped{overflow:hidden!important}.is-overlay{bottom:0;left:0;position:absolute;right:0;top:0}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media screen and (min-width:769px),print{.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1023px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1024px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1216px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1408px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media screen and (min-width:769px),print{.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1023px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1024px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1216px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1408px){.has-text-centered-fullhd{text-align:center!important}}.has-text-justified{text-align:justify!important}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media screen and (min-width:769px),print{.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1023px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1024px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1216px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1408px){.has-text-justified-fullhd{text-align:justify!important}}.has-text-left{text-align:left!important}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media screen and (min-width:769px),print{.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1023px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1024px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1216px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1408px){.has-text-left-fullhd{text-align:left!important}}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media screen and (min-width:769px),print{.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1023px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1024px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1216px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1408px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-text-link{color:#3273dc!important}a.has-text-link:focus,a.has-text-link:hover{color:#205bbc!important}.has-text-info{color:#209cee!important}a.has-text-info:focus,a.has-text-info:hover{color:#0f81cc!important}.has-text-success{color:#23d160!important}a.has-text-success:focus,a.has-text-success:hover{color:#1ca64c!important}.has-text-warning{color:#ffdd57!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd324!important}.has-text-danger{color:#ff3860!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ff0537!important}.has-text-black-bis{color:#121212!important}.has-text-black-ter{color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media screen and (min-width:769px),print{.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1023px){.is-block-touch{display:block!important}}@media screen and (min-width:1024px){.is-block-desktop{display:block!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1216px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1408px){.is-block-fullhd{display:block!important}}.is-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}@media screen and (min-width:769px),print{.is-flex-tablet{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-flex-tablet-only{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}@media screen and (max-width:1023px){.is-flex-touch{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}@media screen and (min-width:1024px){.is-flex-desktop{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-flex-desktop-only{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}@media screen and (min-width:1216px){.is-flex-widescreen{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-flex-widescreen-only{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}@media screen and (min-width:1408px){.is-flex-fullhd{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media screen and (min-width:769px),print{.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1023px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1024px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1216px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1408px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media screen and (min-width:769px),print{.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1023px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1024px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1216px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1408px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media screen and (min-width:769px),print{.is-inline-flex-tablet{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-flex-tablet-only{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media screen and (max-width:1023px){.is-inline-flex-touch{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media screen and (min-width:1024px){.is-inline-flex-desktop{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-flex-desktop-only{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media screen and (min-width:1216px){.is-inline-flex-widescreen{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-flex-widescreen-only{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media screen and (min-width:1408px){.is-inline-flex-fullhd{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}.is-hidden{display:none!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media screen and (min-width:769px),print{.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1023px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1024px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1216px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1408px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media screen and (min-width:769px),print{.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1023px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1024px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1216px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1408px){.is-invisible-fullhd{visibility:hidden!important}}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.is-radiusless{border-radius:0!important}.is-shadowless{-webkit-box-shadow:none!important;box-shadow:none!important}.is-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.box{background-color:#fff;border-radius:5px;-webkit-box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);color:#4a4a4a;display:block;padding:1.25rem}.box:not(:last-child){margin-bottom:1.5rem}a.box:focus,a.box:hover{-webkit-box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px #3273dc;box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px #3273dc}a.box:active{-webkit-box-shadow:inset 0 1px 2px rgba(10,10,10,.2),0 0 0 1px #3273dc;box-shadow:inset 0 1px 2px rgba(10,10,10,.2),0 0 0 1px #3273dc}.button{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid transparent;border-radius:3px;-webkit-box-shadow:none;box-shadow:none;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:1rem;height:2.25em;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.375em - 1px);padding-left:calc(.625em - 1px);padding-right:calc(.625em - 1px);padding-top:calc(.375em - 1px);position:relative;vertical-align:top;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#fff;border-color:#dbdbdb;color:#363636;cursor:pointer;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:.75em;padding-right:.75em;text-align:center;white-space:nowrap}.button.is-active,.button.is-focused,.button:active,.button:focus{outline:0}.button[disabled]{cursor:not-allowed}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.375em - 1px);margin-right:.1875em}.button .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:calc(-.375em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.375em - 1px);margin-right:calc(-.375em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#3273dc;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(50,115,220,.25);box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled]{background-color:transparent;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(255,255,255,.25);box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled]{background-color:#fff;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled]{background-color:#0a0a0a;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#fff}.button.is-white.is-loading:after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined[disabled]{background-color:transparent;border-color:#fff;-webkit-box-shadow:none;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;-webkit-box-shadow:none;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(10,10,10,.25);box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled]{background-color:#0a0a0a;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled]{background-color:#fff;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading:after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;-webkit-box-shadow:none;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;-webkit-box-shadow:none;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:#363636}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:#363636}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:#363636}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(245,245,245,.25);box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:#363636}.button.is-light[disabled]{background-color:#f5f5f5;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-light.is-inverted{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted:hover{background-color:#292929}.button.is-light.is-inverted[disabled]{background-color:#363636;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading:after{border-color:transparent transparent #363636 #363636!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.button.is-light.is-outlined.is-loading:after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;-webkit-box-shadow:none;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#363636;-webkit-box-shadow:none;box-shadow:none;color:#363636}.button.is-dark{background-color:#363636;border-color:transparent;color:#f5f5f5}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(54,54,54,.25);box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#f5f5f5}.button.is-dark[disabled]{background-color:#363636;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-dark.is-inverted{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted:hover{background-color:#e8e8e8}.button.is-dark.is-inverted[disabled]{background-color:#f5f5f5;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#363636}.button.is-dark.is-loading:after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.button.is-dark.is-outlined.is-loading:after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined[disabled]{background-color:transparent;border-color:#363636;-webkit-box-shadow:none;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;-webkit-box-shadow:none;box-shadow:none;color:#f5f5f5}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(0,209,178,.25);box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled]{background-color:#00d1b2;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled]{background-color:#fff;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading:after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined[disabled]{background-color:transparent;border-color:#00d1b2;-webkit-box-shadow:none;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;-webkit-box-shadow:none;box-shadow:none;color:#fff}.button.is-link{background-color:#3273dc;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#276cda;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(50,115,220,.25);box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#2366d1;border-color:transparent;color:#fff}.button.is-link[disabled]{background-color:#3273dc;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#3273dc}.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled]{background-color:#fff;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#3273dc}.button.is-link.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;color:#3273dc}.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#3273dc;border-color:#3273dc;color:#fff}.button.is-link.is-outlined.is-loading:after{border-color:transparent transparent #3273dc #3273dc!important}.button.is-link.is-outlined[disabled]{background-color:transparent;border-color:#3273dc;-webkit-box-shadow:none;box-shadow:none;color:#3273dc}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;-webkit-box-shadow:none;box-shadow:none;color:#fff}.button.is-info{background-color:#209cee;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#1496ed;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(32,156,238,.25);box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#118fe4;border-color:transparent;color:#fff}.button.is-info[disabled]{background-color:#209cee;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#209cee}.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled]{background-color:#fff;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#209cee}.button.is-info.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#209cee;color:#209cee}.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#209cee;border-color:#209cee;color:#fff}.button.is-info.is-outlined.is-loading:after{border-color:transparent transparent #209cee #209cee!important}.button.is-info.is-outlined[disabled]{background-color:transparent;border-color:#209cee;-webkit-box-shadow:none;box-shadow:none;color:#209cee}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#209cee}.button.is-info.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;-webkit-box-shadow:none;box-shadow:none;color:#fff}.button.is-success{background-color:#23d160;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#22c65b;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(35,209,96,.25);box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#20bc56;border-color:transparent;color:#fff}.button.is-success[disabled]{background-color:#23d160;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#23d160}.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled]{background-color:#fff;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#23d160}.button.is-success.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#23d160;color:#23d160}.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#23d160;border-color:#23d160;color:#fff}.button.is-success.is-outlined.is-loading:after{border-color:transparent transparent #23d160 #23d160!important}.button.is-success.is-outlined[disabled]{background-color:transparent;border-color:#23d160;-webkit-box-shadow:none;box-shadow:none;color:#23d160}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#23d160}.button.is-success.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;-webkit-box-shadow:none;box-shadow:none;color:#fff}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(255,221,87,.25);box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled]{background-color:#ffdd57;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled]{background-color:rgba(0,0,0,.7);border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading:after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading:after{border-color:transparent transparent #ffdd57 #ffdd57!important}.button.is-warning.is-outlined[disabled]{background-color:transparent;border-color:#ffdd57;-webkit-box-shadow:none;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:rgba(0,0,0,.7);-webkit-box-shadow:none;box-shadow:none;color:rgba(0,0,0,.7)}.button.is-danger{background-color:#ff3860;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#ff2b56;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){-webkit-box-shadow:0 0 0 .125em rgba(255,56,96,.25);box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ff1f4b;border-color:transparent;color:#fff}.button.is-danger[disabled]{background-color:#ff3860;border-color:transparent;-webkit-box-shadow:none;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled]{background-color:#fff;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;color:#ff3860}.button.is-danger.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;color:#ff3860}.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#ff3860;border-color:#ff3860;color:#fff}.button.is-danger.is-outlined.is-loading:after{border-color:transparent transparent #ff3860 #ff3860!important}.button.is-danger.is-outlined[disabled]{background-color:transparent;border-color:#ff3860;-webkit-box-shadow:none;box-shadow:none;color:#ff3860}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;-webkit-box-shadow:none;box-shadow:none;color:#fff}.button.is-small{border-radius:2px;font-size:.75rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled]{background-color:#fff;border-color:#dbdbdb;-webkit-box-shadow:none;box-shadow:none;opacity:.5}.button.is-fullwidth{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading:after{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em;position:absolute;left:calc(50% - (1em / 2));top:calc(50% - (1em / 2));position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;-webkit-box-shadow:none;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.buttons{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.is-centered{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.buttons.is-right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.container{margin:0 auto;position:relative}@media screen and (min-width:1024px){.container{max-width:960px;width:960px}.container.is-fluid{margin-left:32px;margin-right:32px;max-width:none;width:auto}}@media screen and (max-width:1215px){.container.is-widescreen{max-width:1152px;width:auto}}@media screen and (max-width:1407px){.container.is-fullhd{max-width:1344px;width:auto}}@media screen and (min-width:1216px){.container{max-width:1152px;width:1152px}}@media screen and (min-width:1408px){.container{max-width:1344px;width:1344px}}.content:not(:last-child){margin-bottom:1.5rem}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:400;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style:decimal outside;margin-left:2em;margin-top:1em}.content ul{list-style:disc outside;margin-left:2em;margin-top:1em}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636;text-align:left}.content table tr:hover{background-color:#f5f5f5}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content.is-small{font-size:.75rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.input,.textarea{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid transparent;border-radius:3px;-webkit-box-shadow:none;box-shadow:none;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:1rem;height:2.25em;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.375em - 1px);padding-left:calc(.625em - 1px);padding-right:calc(.625em - 1px);padding-top:calc(.375em - 1px);position:relative;vertical-align:top;background-color:#fff;border-color:#dbdbdb;color:#363636;-webkit-box-shadow:inset 0 1px 2px rgba(10,10,10,.1);box-shadow:inset 0 1px 2px rgba(10,10,10,.1);max-width:100%;width:100%}.input.is-active,.input.is-focused,.input:active,.input:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{outline:0}.input[disabled],.textarea[disabled]{cursor:not-allowed}.input::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input.is-hovered,.input:hover,.textarea.is-hovered,.textarea:hover{border-color:#b5b5b5}.input.is-active,.input.is-focused,.input:active,.input:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{border-color:#3273dc;-webkit-box-shadow:0 0 0 .125em rgba(50,115,220,.25);box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input[disabled],.textarea[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;-webkit-box-shadow:none;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.textarea[disabled]::-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-moz-placeholder,.textarea[disabled]:-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder{color:rgba(122,122,122,.3)}.input[readonly],.textarea[readonly]{-webkit-box-shadow:none;box-shadow:none}.input.is-white,.textarea.is-white{border-color:#fff}.input.is-white.is-active,.input.is-white.is-focused,.input.is-white:active,.input.is-white:focus,.textarea.is-white.is-active,.textarea.is-white.is-focused,.textarea.is-white:active,.textarea.is-white:focus{-webkit-box-shadow:0 0 0 .125em rgba(255,255,255,.25);box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.input.is-black,.textarea.is-black{border-color:#0a0a0a}.input.is-black.is-active,.input.is-black.is-focused,.input.is-black:active,.input.is-black:focus,.textarea.is-black.is-active,.textarea.is-black.is-focused,.textarea.is-black:active,.textarea.is-black:focus{-webkit-box-shadow:0 0 0 .125em rgba(10,10,10,.25);box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.input.is-light,.textarea.is-light{border-color:#f5f5f5}.input.is-light.is-active,.input.is-light.is-focused,.input.is-light:active,.input.is-light:focus,.textarea.is-light.is-active,.textarea.is-light.is-focused,.textarea.is-light:active,.textarea.is-light:focus{-webkit-box-shadow:0 0 0 .125em rgba(245,245,245,.25);box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.input.is-dark,.textarea.is-dark{border-color:#363636}.input.is-dark.is-active,.input.is-dark.is-focused,.input.is-dark:active,.input.is-dark:focus,.textarea.is-dark.is-active,.textarea.is-dark.is-focused,.textarea.is-dark:active,.textarea.is-dark:focus{-webkit-box-shadow:0 0 0 .125em rgba(54,54,54,.25);box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.input.is-primary,.textarea.is-primary{border-color:#00d1b2}.input.is-primary.is-active,.input.is-primary.is-focused,.input.is-primary:active,.input.is-primary:focus,.textarea.is-primary.is-active,.textarea.is-primary.is-focused,.textarea.is-primary:active,.textarea.is-primary:focus{-webkit-box-shadow:0 0 0 .125em rgba(0,209,178,.25);box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.input.is-link,.textarea.is-link{border-color:#3273dc}.input.is-link.is-active,.input.is-link.is-focused,.input.is-link:active,.input.is-link:focus,.textarea.is-link.is-active,.textarea.is-link.is-focused,.textarea.is-link:active,.textarea.is-link:focus{-webkit-box-shadow:0 0 0 .125em rgba(50,115,220,.25);box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input.is-info,.textarea.is-info{border-color:#209cee}.input.is-info.is-active,.input.is-info.is-focused,.input.is-info:active,.input.is-info:focus,.textarea.is-info.is-active,.textarea.is-info.is-focused,.textarea.is-info:active,.textarea.is-info:focus{-webkit-box-shadow:0 0 0 .125em rgba(32,156,238,.25);box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.input.is-success,.textarea.is-success{border-color:#23d160}.input.is-success.is-active,.input.is-success.is-focused,.input.is-success:active,.input.is-success:focus,.textarea.is-success.is-active,.textarea.is-success.is-focused,.textarea.is-success:active,.textarea.is-success:focus{-webkit-box-shadow:0 0 0 .125em rgba(35,209,96,.25);box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.input.is-warning,.textarea.is-warning{border-color:#ffdd57}.input.is-warning.is-active,.input.is-warning.is-focused,.input.is-warning:active,.input.is-warning:focus,.textarea.is-warning.is-active,.textarea.is-warning.is-focused,.textarea.is-warning:active,.textarea.is-warning:focus{-webkit-box-shadow:0 0 0 .125em rgba(255,221,87,.25);box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.input.is-danger,.textarea.is-danger{border-color:#ff3860}.input.is-danger.is-active,.input.is-danger.is-focused,.input.is-danger:active,.input.is-danger:focus,.textarea.is-danger.is-active,.textarea.is-danger.is-focused,.textarea.is-danger:active,.textarea.is-danger:focus{-webkit-box-shadow:0 0 0 .125em rgba(255,56,96,.25);box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.input.is-small,.textarea.is-small{border-radius:2px;font-size:.75rem}.input.is-medium,.textarea.is-medium{font-size:1.25rem}.input.is-large,.textarea.is-large{font-size:1.5rem}.input.is-fullwidth,.textarea.is-fullwidth{display:block;width:100%}.input.is-inline,.textarea.is-inline{display:inline;width:auto}.input.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.input.is-static{background-color:transparent;border-color:transparent;-webkit-box-shadow:none;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:.625em;resize:vertical}.textarea:not([rows]){max-height:600px;min-height:120px}.textarea[rows]{height:unset}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox[disabled],.radio[disabled]{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.25em}.select:not(.is-multiple)::after{border:1px solid #3273dc;border-right:0;border-top:0;content:" ";display:block;height:.5em;pointer-events:none;position:absolute;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:center;transform-origin:center;width:.5em;margin-top:-.375em;right:1.125em;top:50%;z-index:4}.select.is-rounded select{border-radius:290486px;padding-left:1em}.select select{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid transparent;border-radius:3px;-webkit-box-shadow:none;box-shadow:none;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:1rem;height:2.25em;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.375em - 1px);padding-left:calc(.625em - 1px);padding-right:calc(.625em - 1px);padding-top:calc(.375em - 1px);position:relative;vertical-align:top;background-color:#fff;border-color:#dbdbdb;color:#363636;cursor:pointer;display:block;font-size:1em;max-width:100%;outline:0}.select select.is-active,.select select.is-focused,.select select:active,.select select:focus{outline:0}.select select[disabled]{cursor:not-allowed}.select select::-moz-placeholder{color:rgba(54,54,54,.3)}.select select::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.select select:-moz-placeholder{color:rgba(54,54,54,.3)}.select select:-ms-input-placeholder{color:rgba(54,54,54,.3)}.select select.is-hovered,.select select:hover{border-color:#b5b5b5}.select select.is-active,.select select.is-focused,.select select:active,.select select:focus{border-color:#3273dc;-webkit-box-shadow:0 0 0 .125em rgba(50,115,220,.25);box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select select[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;-webkit-box-shadow:none;box-shadow:none;color:#7a7a7a}.select select[disabled]::-moz-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]:-moz-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]:-ms-input-placeholder{color:rgba(122,122,122,.3)}.select select::-ms-expand{display:none}.select select[disabled]:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:unset;padding:0}.select select[multiple] option{padding:.5em 1em}.select:hover::after{border-color:#363636}.select.is-white select{border-color:#fff}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{-webkit-box-shadow:0 0 0 .125em rgba(255,255,255,.25);box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{-webkit-box-shadow:0 0 0 .125em rgba(10,10,10,.25);box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{-webkit-box-shadow:0 0 0 .125em rgba(245,245,245,.25);box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.select.is-dark select{border-color:#363636}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{-webkit-box-shadow:0 0 0 .125em rgba(54,54,54,.25);box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{-webkit-box-shadow:0 0 0 .125em rgba(0,209,178,.25);box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link select{border-color:#3273dc}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{-webkit-box-shadow:0 0 0 .125em rgba(50,115,220,.25);box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select.is-info select{border-color:#209cee}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{-webkit-box-shadow:0 0 0 .125em rgba(32,156,238,.25);box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.select.is-success select{border-color:#23d160}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{-webkit-box-shadow:0 0 0 .125em rgba(35,209,96,.25);box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.select.is-warning select{border-color:#ffdd57}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{-webkit-box-shadow:0 0 0 .125em rgba(255,221,87,.25);box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.select.is-danger select{border-color:#ff3860}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{-webkit-box-shadow:0 0 0 .125em rgba(255,56,96,.25);box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled::after{border-color:#7a7a7a}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select.is-loading::after{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em;margin-top:0;position:absolute;right:.625em;top:.625em;-webkit-transform:none;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(255,255,255,.25);box-shadow:0 0 .5em rgba(255,255,255,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(10,10,10,.25);box-shadow:0 0 .5em rgba(10,10,10,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:#363636}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:#363636}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(245,245,245,.25);box-shadow:0 0 .5em rgba(245,245,245,.25);color:#363636}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:#363636}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#f5f5f5}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(54,54,54,.25);box-shadow:0 0 .5em rgba(54,54,54,.25);color:#f5f5f5}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#f5f5f5}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(0,209,178,.25);box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#3273dc;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#276cda;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(50,115,220,.25);box-shadow:0 0 .5em rgba(50,115,220,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#2366d1;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#209cee;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#1496ed;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(32,156,238,.25);box-shadow:0 0 .5em rgba(32,156,238,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#118fe4;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#23d160;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#22c65b;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(35,209,96,.25);box-shadow:0 0 .5em rgba(35,209,96,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#20bc56;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(255,221,87,.25);box-shadow:0 0 .5em rgba(255,221,87,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#ff3860;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#ff2b56;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;-webkit-box-shadow:0 0 .5em rgba(255,56,96,.25);box-shadow:0 0 .5em rgba(255,56,96,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ff1f4b;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:3px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.file.is-boxed .file-cta{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:3px 3px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 3px 3px;border-width:0 1px 1px}.file.is-centered{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:none}.file.is-right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.file.is-right .file-cta{border-radius:0 3px 3px 0}.file.is-right .file-name{border-radius:3px 0 0 3px;border-width:1px 0 1px 1px;-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.file-label{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;cursor:pointer;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:.01em;left:0;outline:0;position:absolute;top:0;width:.01em}.file-cta,.file-name{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid transparent;border-radius:3px;-webkit-box-shadow:none;box-shadow:none;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:1rem;height:2.25em;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.375em - 1px);padding-left:calc(.625em - 1px);padding-right:calc(.625em - 1px);padding-top:calc(.375em - 1px);position:relative;vertical-align:top;border-color:#dbdbdb;border-radius:3px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta.is-active,.file-cta.is-focused,.file-cta:active,.file-cta:focus,.file-name.is-active,.file-name.is-focused,.file-name:active,.file-name:focus{outline:0}.file-cta[disabled],.file-name[disabled]{cursor:not-allowed}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:left;text-overflow:ellipsis}.file-icon{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;height:1em;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#3273dc}.help.is-info{color:#209cee}.help.is-success{color:#23d160}.help.is-warning{color:#ffdd57}.help.is-danger{color:#ff3860}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}.field.has-addons .control:first-child .button,.field.has-addons .control:first-child .input,.field.has-addons .control:first-child .select select{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child .button,.field.has-addons .control:last-child .input,.field.has-addons .control:last-child .select select{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button.is-hovered,.field.has-addons .control .button:hover,.field.has-addons .control .input.is-hovered,.field.has-addons .control .input:hover,.field.has-addons .control .select select.is-hovered,.field.has-addons .control .select select:hover{z-index:2}.field.has-addons .control .button.is-active,.field.has-addons .control .button.is-focused,.field.has-addons .control .button:active,.field.has-addons .control .button:focus,.field.has-addons .control .input.is-active,.field.has-addons .control .input.is-focused,.field.has-addons .control .input:active,.field.has-addons .control .input:focus,.field.has-addons .control .select select.is-active,.field.has-addons .control .select select.is-focused,.field.has-addons .control .select select:active,.field.has-addons .control .select select:focus{z-index:3}.field.has-addons .control .button.is-active:hover,.field.has-addons .control .button.is-focused:hover,.field.has-addons .control .button:active:hover,.field.has-addons .control .button:focus:hover,.field.has-addons .control .input.is-active:hover,.field.has-addons .control .input.is-focused:hover,.field.has-addons .control .input:active:hover,.field.has-addons .control .input:focus:hover,.field.has-addons .control .select select.is-active:hover,.field.has-addons .control .select select.is-focused:hover,.field.has-addons .control .select select:active:hover,.field.has-addons .control .select select:focus:hover{z-index:4}.field.has-addons .control.is-expanded{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.field.has-addons.has-addons-centered{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.field.has-addons.has-addons-right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0}.field.is-grouped{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.field.is-grouped>.control{-ms-flex-negative:0;flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.field.is-grouped.is-grouped-right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.field.is-grouped.is-grouped-multiline{-ms-flex-wrap:wrap;flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width:769px),print{.field.is-horizontal{display:-webkit-box;display:-ms-flexbox;display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media screen and (min-width:769px),print{.field-label{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media screen and (min-width:769px),print{.field-body{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:5;-ms-flex-positive:5;flex-grow:5;-ms-flex-negative:1;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{-ms-flex-negative:1;flex-shrink:1}.field-body>.field:not(.is-narrow){-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{font-size:1rem;position:relative;text-align:left}.control.has-icon .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icon .input:focus+.icon{color:#7a7a7a}.control.has-icon .input.is-small+.icon{font-size:.75rem}.control.has-icon .input.is-medium+.icon{font-size:1.25rem}.control.has-icon .input.is-large+.icon{font-size:1.5rem}.control.has-icon:not(.has-icon-right) .icon{left:0}.control.has-icon:not(.has-icon-right) .input{padding-left:2.25em}.control.has-icon.has-icon-right .icon{right:0}.control.has-icon.has-icon-right .input{padding-right:2.25em}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon{color:#7a7a7a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select{padding-left:2.25em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select{padding-right:2.25em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading::after{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em;position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.icon{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:290486px}.image.is-16by9 img,.image.is-1by1 img,.image.is-2by1 img,.image.is-3by2 img,.image.is-4by3 img,.image.is-square img{bottom:0;left:0;position:absolute;right:0;top:0;height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:3px;padding:1.25rem 2.5rem 1.25rem 1.5rem;position:relative}.notification:not(:last-child){margin-bottom:1.5rem}.notification a:not(.button){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:0 0}.notification>.delete{position:absolute;right:.5rem;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:#363636}.notification.is-dark{background-color:#363636;color:#f5f5f5}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-link{background-color:#3273dc;color:#fff}.notification.is-info{background-color:#209cee;color:#fff}.notification.is-success{background-color:#23d160;color:#fff}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.notification.is-danger{background-color:#ff3860;color:#fff}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress:not(:last-child){margin-bottom:1.5rem}.progress::-webkit-progress-bar{background-color:#dbdbdb}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-link::-webkit-progress-value{background-color:#3273dc}.progress.is-link::-moz-progress-bar{background-color:#3273dc}.progress.is-link::-ms-fill{background-color:#3273dc}.progress.is-info::-webkit-progress-value{background-color:#209cee}.progress.is-info::-moz-progress-bar{background-color:#209cee}.progress.is-info::-ms-fill{background-color:#209cee}.progress.is-success::-webkit-progress-value{background-color:#23d160}.progress.is-success::-moz-progress-bar{background-color:#23d160}.progress.is-success::-ms-fill{background-color:#23d160}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-danger::-webkit-progress-value{background-color:#ff3860}.progress.is-danger::-moz-progress-bar{background-color:#ff3860}.progress.is-danger::-ms-fill{background-color:#ff3860}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}.table{background-color:#fff;color:#363636;margin-bottom:1.5rem}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#f5f5f5}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#3273dc;border-color:#3273dc;color:#fff}.table td.is-info,.table th.is-info{background-color:#209cee;border-color:#209cee;color:#fff}.table td.is-success,.table th.is-success{background-color:#23d160;border-color:#23d160;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#ff3860;border-color:#ff3860;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table th{color:#363636;text-align:left}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(even){background-color:#fafafa}.tags{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.tags.has-addons .tag:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.tags.is-centered{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tag:not(body){-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#f5f5f5;border-radius:3px;color:#4a4a4a;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:.75rem;height:2em;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25em;margin-right:-.375em}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:#363636}.tag:not(body).is-dark{background-color:#363636;color:#f5f5f5}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-link{background-color:#3273dc;color:#fff}.tag:not(body).is-info{background-color:#209cee;color:#fff}.tag:not(body).is-success{background-color:#23d160;color:#fff}.tag:not(body).is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.tag:not(body).is-danger{background-color:#ff3860;color:#fff}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete:after,.tag:not(body).is-delete:before{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.tag:not(body).is-delete:before{height:1px;width:50%}.tag:not(body).is-delete:after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:290486px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.title sub{font-size:.75em}.subtitle sup,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title+.highlight{margin-top:-.75rem}.title:not(.is-spaced)+.subtitle{margin-top:-1.5rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.5rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.block:not(:last-child){margin-bottom:1.5rem}.delete{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,.2);border:none;border-radius:290486px;cursor:pointer;display:inline-block;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:0;position:relative;vertical-align:top;width:20px}.delete:after,.delete:before{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.delete:before{height:2px;width:50%}.delete:after{height:50%;width:2px}.delete:focus,.delete:hover{background-color:rgba(10,10,10,.3)}.delete:active{background-color:rgba(10,10,10,.4)}.delete.is-small{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.delete.is-medium{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.delete.is-large{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-weight:400;max-width:100%;overflow:hidden;padding:0}.highlight:not(:last-child){margin-bottom:1.5rem}.highlight pre{overflow:auto;max-width:100%}.loader{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}.number{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#f5f5f5;border-radius:290486px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:1.25rem;height:2em;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.breadcrumb{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;font-size:1rem;overflow:hidden;overflow-x:auto;white-space:nowrap}.breadcrumb:not(:last-child){margin-bottom:1.5rem}.breadcrumb a{-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#3273dc;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:.5em .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li::before{color:#4a4a4a;content:"\0002f"}.breadcrumb ol,.breadcrumb ul{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li::before{content:"\02192"}.breadcrumb.has-bullet-separator li+li::before{content:"\02022"}.breadcrumb.has-dot-separator li+li::before{content:"\000b7"}.breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}.card{background-color:#fff;-webkit-box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);color:#4a4a4a;max-width:100%;position:relative}.card-header{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-shadow:0 1px 2px rgba(10,10,10,.1);box-shadow:0 1px 2px rgba(10,10,10,.1);display:-webkit-box;display:-ms-flexbox;display:flex}.card-header-title{-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#363636;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;font-weight:700;padding:.75rem}.card-header-title.is-centered{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.card-header-icon{-webkit-box-align:center;-ms-flex-align:center;align-items:center;cursor:pointer;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:.75rem}.card-image{display:block;position:relative}.card-content{padding:1.5rem}.card-footer{border-top:1px solid #dbdbdb;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex}.card-footer-item{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #dbdbdb}.card .media:not(:last-child){margin-bottom:.75rem}.dropdown{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:unset;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:3px;-webkit-box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}a.dropdown-item{padding-right:3rem;white-space:nowrap}a.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}a.dropdown-item.is-active{background-color:#3273dc;color:#fff}.dropdown-divider{background-color:#dbdbdb;border:none;display:block;height:1px;margin:.5rem 0}.level{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.level:not(:last-child){margin-bottom:1.5rem}.level code{border-radius:3px}.level img{display:inline-block;vertical-align:top}.level.is-mobile{display:-webkit-box;display:-ms-flexbox;display:flex}.level.is-mobile .level-left,.level.is-mobile .level-right{display:-webkit-box;display:-ms-flexbox;display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item{margin-right:.75rem}.level.is-mobile .level-item:not(:last-child){margin-bottom:0}.level.is-mobile .level-item:not(.is-narrow){-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}@media screen and (min-width:769px),print{.level{display:-webkit-box;display:-ms-flexbox;display:flex}.level>.level-item:not(.is-narrow){-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}}.level-item{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1}@media screen and (min-width:769px),print{.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width:769px),print{.level-left{display:-webkit-box;display:-ms-flexbox;display:flex}}.level-right{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}@media screen and (min-width:769px),print{.level-right{display:-webkit-box;display:-ms-flexbox;display:flex}}.media{-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;display:-webkit-box;display:-ms-flexbox;display:flex;text-align:left}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid rgba(219,219,219,.5);display:-webkit-box;display:-ms-flexbox;display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid rgba(219,219,219,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{-ms-flex-preferred-size:auto;flex-basis:auto;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1;overflow:auto;text-align:left}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#3273dc;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:3px;font-size:1rem}.message:not(:last-child){margin-bottom:1.5rem}.message strong{color:currentColor}.message a:not(.button):not(.tag){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff;color:#4d4d4d}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a;color:#090909}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:#363636}.message.is-light .message-body{border-color:#f5f5f5;color:#505050}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#f5f5f5}.message.is-dark .message-body{border-color:#363636;color:#2a2a2a}.message.is-primary{background-color:#f5fffd}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#021310}.message.is-link{background-color:#f6f9fe}.message.is-link .message-header{background-color:#3273dc;color:#fff}.message.is-link .message-body{border-color:#3273dc;color:#22509a}.message.is-info{background-color:#f6fbfe}.message.is-info .message-header{background-color:#209cee;color:#fff}.message.is-info .message-body{border-color:#209cee;color:#12537e}.message.is-success{background-color:#f6fef9}.message.is-success .message-header{background-color:#23d160;color:#fff}.message.is-success .message-body{border-color:#23d160;color:#0e301a}.message.is-warning{background-color:#fffdf5}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#3b3108}.message.is-danger{background-color:#fff5f7}.message.is-danger .message-header{background-color:#ff3860;color:#fff}.message.is-danger .message-body{border-color:#ff3860;color:#cd0930}.message-header{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#4a4a4a;border-radius:3px 3px 0 0;color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;line-height:1.25;padding:.5em .75em;position:relative}.message-header .delete{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-top-left-radius:0;border-top-right-radius:0;border-top:none}.message-body{border:1px solid #dbdbdb;border-radius:3px;color:#4a4a4a;padding:1em 1.25em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{bottom:0;left:0;position:absolute;right:0;top:0;-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:none;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:-webkit-box;display:-ms-flexbox;display:flex}.modal-background{bottom:0;left:0;position:absolute;right:0;top:0;background-color:rgba(10,10,10,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width:769px),print{.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,.2);border:none;border-radius:290486px;cursor:pointer;display:inline-block;-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:0;position:relative;vertical-align:top;width:20px;background:0 0;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-close:after,.modal-close:before{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.modal-close:before{height:2px;width:50%}.modal-close:after{height:50%;width:2px}.modal-close:focus,.modal-close:hover{background-color:rgba(10,10,10,.3)}.modal-close:active{background-color:rgba(10,10,10,.4)}.modal-close.is-small{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.modal-close.is-medium{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.modal-close.is-large{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.modal-card{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden}.modal-card-foot,.modal-card-head{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#f5f5f5;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-negative:0;flex-shrink:0;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:5px;border-top-right-radius:5px}.modal-card-title{color:#363636;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:10px}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}@media screen and (min-width:1024px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link::after,.navbar.is-white .navbar-start .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1024px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link::after,.navbar.is-black .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:#363636}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:#363636}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-brand .navbar-link::after{border-color:#363636}@media screen and (min-width:1024px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:#363636}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-end .navbar-link::after,.navbar.is-light .navbar-start .navbar-link::after{border-color:#363636}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#363636}}.navbar.is-dark{background-color:#363636;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link::after{border-color:#f5f5f5}@media screen and (min-width:1024px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link::after,.navbar.is-dark .navbar-start .navbar-link::after{border-color:#f5f5f5}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#f5f5f5}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1024px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link::after,.navbar.is-primary .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#3273dc;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1024px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-end .navbar-link::after,.navbar.is-link .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#3273dc;color:#fff}}.navbar.is-info{background-color:#209cee;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1024px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-end .navbar-link::after,.navbar.is-info .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#209cee;color:#fff}}.navbar.is-success{background-color:#23d160;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1024px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-end .navbar-link::after,.navbar.is-success .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#23d160;color:#fff}}.navbar.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}@media screen and (min-width:1024px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link::after,.navbar.is-warning .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#ff3860;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1024px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-end .navbar-link::after,.navbar.is-danger .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#ff3860;color:#fff}}.navbar>.container{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{-webkit-box-shadow:0 2px 3px rgba(10,10,10,.1);box-shadow:0 2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{-webkit-box-shadow:0 -2px 3px rgba(10,10,10,.1);box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top{top:0}html.has-navbar-fixed-top{padding-top:3.25rem}html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-negative:0;flex-shrink:0;min-height:3.25rem}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;-webkit-transform-origin:center;transform-origin:center;-webkit-transition-duration:86ms;transition-duration:86ms;-webkit-transition-property:background-color,opacity,-webkit-transform;transition-property:background-color,opacity,-webkit-transform;transition-property:background-color,opacity,transform;transition-property:background-color,opacity,transform,-webkit-transform;-webkit-transition-timing-function:ease-out;transition-timing-function:ease-out;width:16px}.navbar-burger span:nth-child(1){top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:nth-child(1){-webkit-transform:translateY(5px) rotate(45deg);transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){-webkit-transform:translateY(-5px) rotate(-45deg);transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem 1rem;position:relative}a.navbar-item.is-active,a.navbar-item:hover,a.navbar-link.is-active,a.navbar-link:hover{background-color:#f5f5f5;color:#3273dc}.navbar-item{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#3273dc}.navbar-item.is-tab.is-active{background-color:transparent;border-bottom-color:#3273dc;border-bottom-style:solid;border-bottom-width:3px;color:#3273dc;padding-bottom:calc(.5rem - 3px)}.navbar-content{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.navbar-link{padding-right:2.5em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#dbdbdb;border:none;display:none;height:1px;margin:.5rem 0}@media screen and (max-width:1023px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex}.navbar-menu{background-color:#fff;-webkit-box-shadow:0 8px 16px rgba(10,10,10,.1);box-shadow:0 8px 16px rgba(10,10,10,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{-webkit-box-shadow:0 -2px 3px rgba(10,10,10,.1);box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top .navbar-menu,.navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}html.has-navbar-fixed-top-touch{padding-top:3.25rem}html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1024px){.navbar,.navbar-end,.navbar-menu,.navbar-start{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex}.navbar{min-height:3.25rem}.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:hover,.navbar.is-transparent a.navbar-link.is-active,.navbar.is-transparent a.navbar-link:hover{background-color:transparent!important}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-burger{display:none}.navbar-item,.navbar-link{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex}.navbar-item.has-dropdown{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.navbar-item.has-dropdown-up .navbar-link::after{-webkit-transform:rotate(135deg) translate(.25em,-.25em);transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:1px solid #dbdbdb;border-radius:5px 5px 0 0;border-top:none;bottom:100%;-webkit-box-shadow:0 -8px 8px rgba(10,10,10,.1);box-shadow:0 -8px 8px rgba(10,10,10,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed{opacity:1;pointer-events:auto;-webkit-transform:translateY(0);transform:translateY(0)}.navbar-link::after{border:1px solid #3273dc;border-right:0;border-top:0;content:" ";display:block;height:.5em;pointer-events:none;position:absolute;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:center;transform-origin:center;width:.5em;margin-top:-.375em;right:1.125em;top:50%}.navbar-menu{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0}.navbar-start{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;margin-right:auto}.navbar-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-top:1px solid #dbdbdb;-webkit-box-shadow:0 8px 8px rgba(10,10,10,.1);box-shadow:0 8px 8px rgba(10,10,10,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-dropdown.is-boxed{border-radius:5px;border-top:none;-webkit-box-shadow:0 8px 8px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);box-shadow:0 8px 8px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));-webkit-transform:translateY(-5px);transform:translateY(-5px);-webkit-transition-duration:86ms;transition-duration:86ms;-webkit-transition-property:opacity,-webkit-transform;transition-property:opacity,-webkit-transform;transition-property:opacity,transform;transition-property:opacity,transform,-webkit-transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-1rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-1rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{-webkit-box-shadow:0 -2px 3px rgba(10,10,10,.1);box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-desktop{top:0}html.has-navbar-fixed-top-desktop{padding-top:3.25rem}html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}a.navbar-item.is-active,a.navbar-link.is-active{color:#0a0a0a}a.navbar-item.is-active:not(:hover),a.navbar-link.is-active:not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#f5f5f5}}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:290486px}.pagination.is-rounded .pagination-link{border-radius:290486px}.pagination,.pagination-list{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid transparent;border-radius:3px;-webkit-box-shadow:none;box-shadow:none;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:1rem;height:2.25em;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.375em - 1px);padding-left:calc(.625em - 1px);padding-right:calc(.625em - 1px);padding-top:calc(.375em - 1px);position:relative;vertical-align:top;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:1em;padding-left:.5em;padding-right:.5em;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin:.25rem;text-align:center}.pagination-ellipsis.is-active,.pagination-ellipsis.is-focused,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link.is-active,.pagination-link.is-focused,.pagination-link:active,.pagination-link:focus,.pagination-next.is-active,.pagination-next.is-focused,.pagination-next:active,.pagination-next:focus,.pagination-previous.is-active,.pagination-previous.is-focused,.pagination-previous:active,.pagination-previous:focus{outline:0}.pagination-ellipsis[disabled],.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{cursor:not-allowed}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.25em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#3273dc}.pagination-link:active,.pagination-next:active,.pagination-previous:active{-webkit-box-shadow:inset 0 1px 2px rgba(10,10,10,.2);box-shadow:inset 0 1px 2px rgba(10,10,10,.2)}.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;-webkit-box-shadow:none;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#3273dc;border-color:#3273dc;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{-ms-flex-wrap:wrap;flex-wrap:wrap}@media screen and (max-width:768px){.pagination{-ms-flex-wrap:wrap;flex-wrap:wrap}.pagination-next,.pagination-previous{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.pagination-list li{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}}@media screen and (min-width:769px),print{.pagination-list{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.pagination-previous{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.pagination-next{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.pagination{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.pagination.is-centered .pagination-previous{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.pagination.is-centered .pagination-list{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.pagination.is-centered .pagination-next{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.pagination.is-right .pagination-previous{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.pagination.is-right .pagination-next{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.pagination.is-right .pagination-list{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}}.panel{font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel-block,.panel-heading,.panel-tabs{border-bottom:1px solid #dbdbdb;border-left:1px solid #dbdbdb;border-right:1px solid #dbdbdb}.panel-block:first-child,.panel-heading:first-child,.panel-tabs:first-child{border-top:1px solid #dbdbdb}.panel-heading{background-color:#f5f5f5;border-radius:3px 3px 0 0;color:#363636;font-size:1.25em;font-weight:300;line-height:1.25;padding:.5em .75em}.panel-tabs{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end;display:-webkit-box;display:-ms-flexbox;display:flex;font-size:.875em;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#3273dc}.panel-block{-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:#363636;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{-ms-flex-wrap:wrap;flex-wrap:wrap}.panel-block.is-active{border-left-color:#3273dc;color:#363636}.panel-block.is-active .panel-icon{color:#3273dc}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;font-size:1rem;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs:not(:last-child){margin-bottom:1.5rem}.tabs a{-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#3273dc;color:#3273dc}.tabs ul{-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{-webkit-box-flex:0;-ms-flex:none;flex:none;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:.75em;padding-right:.75em}.tabs ul.is-right{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.tabs.is-right ul{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:3px 3px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0}.tabs.is-toggle a{border-color:#dbdbdb;border-style:solid;border-width:1px;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-radius:3px 0 0 3px}.tabs.is-toggle li:last-child a{border-radius:0 3px 3px 0}.tabs.is-toggle li.is-active a{background-color:#3273dc;border-color:#3273dc;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:290486px;border-top-left-radius:290486px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:290486px;border-top-right-radius:290486px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{-webkit-box-flex:0;-ms-flex:none;flex:none}.columns.is-mobile>.column.is-full{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.columns.is-mobile>.column.is-one-third{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{-webkit-box-flex:0;-ms-flex:none;flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{-webkit-box-flex:0;-ms-flex:none;flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{-webkit-box-flex:0;-ms-flex:none;flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{-webkit-box-flex:0;-ms-flex:none;flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-1{-webkit-box-flex:0;-ms-flex:none;flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{-webkit-box-flex:0;-ms-flex:none;flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{-webkit-box-flex:0;-ms-flex:none;flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{-webkit-box-flex:0;-ms-flex:none;flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{-webkit-box-flex:0;-ms-flex:none;flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{-webkit-box-flex:0;-ms-flex:none;flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none}.column.is-full-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-three-quarters-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-two-thirds-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.6666%}.column.is-half-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-one-third-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.3333%}.column.is-one-quarter-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-one-fifth-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:20%}.column.is-two-fifths-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:40%}.column.is-three-fifths-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:60%}.column.is-four-fifths-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-1-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width:769px),print{.column.is-narrow,.column.is-narrow-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none}.column.is-full,.column.is-full-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-1,.column.is-1-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1023px){.column.is-narrow-touch{-webkit-box-flex:0;-ms-flex:none;flex:none}.column.is-full-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-three-quarters-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-two-thirds-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.6666%}.column.is-half-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-one-third-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.3333%}.column.is-one-quarter-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-one-fifth-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:20%}.column.is-two-fifths-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:40%}.column.is-three-fifths-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:60%}.column.is-four-fifths-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-1-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1024px){.column.is-narrow-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none}.column.is-full-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-three-quarters-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-two-thirds-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.6666%}.column.is-half-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-one-third-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.3333%}.column.is-one-quarter-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-one-fifth-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:20%}.column.is-two-fifths-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:40%}.column.is-three-fifths-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:60%}.column.is-four-fifths-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-1-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1216px){.column.is-narrow-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none}.column.is-full-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-three-quarters-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-two-thirds-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.6666%}.column.is-half-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-one-third-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.3333%}.column.is-one-quarter-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-one-fifth-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:20%}.column.is-two-fifths-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:40%}.column.is-three-fifths-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:60%}.column.is-four-fifths-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-1-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1408px){.column.is-narrow-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none}.column.is-full-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-three-quarters-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-two-thirds-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.6666%}.column.is-half-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-one-third-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.3333%}.column.is-one-quarter-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-one-fifth-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:20%}.column.is-two-fifths-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:40%}.column.is-three-fifths-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:60%}.column.is-four-fifths-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-1-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}.columns.is-centered{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:-webkit-box;display:-ms-flexbox;display:flex}.columns.is-multiline{-ms-flex-wrap:wrap;flex-wrap:wrap}.columns.is-vcentered{-webkit-box-align:center;-ms-flex-align:center;align-items:center}@media screen and (min-width:769px),print{.columns:not(.is-desktop){display:-webkit-box;display:-ms-flexbox;display:flex}}@media screen and (min-width:1024px){.columns.is-desktop{display:-webkit-box;display:-ms-flexbox;display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable .column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}.columns.is-variable.is-1{--columnGap:0.25rem}.columns.is-variable.is-2{--columnGap:0.5rem}.columns.is-variable.is-3{--columnGap:0.75rem}.columns.is-variable.is-4{--columnGap:1rem}.columns.is-variable.is-5{--columnGap:1.25rem}.columns.is-variable.is-6{--columnGap:1.5rem}.columns.is-variable.is-7{--columnGap:1.75rem}.columns.is-variable.is-8{--columnGap:2rem}.tile{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:block;-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1;min-height:-webkit-min-content;min-height:-moz-min-content;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media screen and (min-width:769px),print{.tile:not(.is-child){display:-webkit-box;display:-ms-flexbox;display:flex}.tile.is-1{-webkit-box-flex:0;-ms-flex:none;flex:none;width:8.33333%}.tile.is-2{-webkit-box-flex:0;-ms-flex:none;flex:none;width:16.66667%}.tile.is-3{-webkit-box-flex:0;-ms-flex:none;flex:none;width:25%}.tile.is-4{-webkit-box-flex:0;-ms-flex:none;flex:none;width:33.33333%}.tile.is-5{-webkit-box-flex:0;-ms-flex:none;flex:none;width:41.66667%}.tile.is-6{-webkit-box-flex:0;-ms-flex:none;flex:none;width:50%}.tile.is-7{-webkit-box-flex:0;-ms-flex:none;flex:none;width:58.33333%}.tile.is-8{-webkit-box-flex:0;-ms-flex:none;flex:none;width:66.66667%}.tile.is-9{-webkit-box-flex:0;-ms-flex:none;flex:none;width:75%}.tile.is-10{-webkit-box-flex:0;-ms-flex:none;flex:none;width:83.33333%}.tile.is-11{-webkit-box-flex:0;-ms-flex:none;flex:none;width:91.66667%}.tile.is-12{-webkit-box-flex:0;-ms-flex:none;flex:none;width:100%}}.hero{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.hero .navbar{background:0 0}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.tag),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:rgba(10,10,10,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1023px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:rgba(10,10,10,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover{opacity:1}.hero.is-white .tabs li.is-active a{opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.tag),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:rgba(255,255,255,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:rgba(255,255,255,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover{opacity:1}.hero.is-black .tabs li.is-active a{opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}}.hero.is-light{background-color:#f5f5f5;color:#363636}.hero.is-light a:not(.button):not(.dropdown-item):not(.tag),.hero.is-light strong{color:inherit}.hero.is-light .title{color:#363636}.hero.is-light .subtitle{color:rgba(54,54,54,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:#363636}@media screen and (max-width:1023px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(54,54,54,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.hero.is-light .tabs a{color:#363636;opacity:.9}.hero.is-light .tabs a:hover{opacity:1}.hero.is-light .tabs li.is-active a{opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:#363636}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}}.hero.is-dark{background-color:#363636;color:#f5f5f5}.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#f5f5f5}.hero.is-dark .subtitle{color:rgba(245,245,245,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#f5f5f5}@media screen and (max-width:1023px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:rgba(245,245,245,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.hero.is-dark .tabs a{color:#f5f5f5;opacity:.9}.hero.is-dark .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#f5f5f5}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:rgba(255,255,255,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:rgba(255,255,255,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}}.hero.is-link{background-color:#3273dc;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.tag),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:rgba(255,255,255,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-link .navbar-menu{background-color:#3273dc}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:rgba(255,255,255,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#2366d1;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover{opacity:1}.hero.is-link .tabs li.is-active a{opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3273dc}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#1577c6 0,#3273dc 71%,#4366e5 100%)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1577c6 0,#3273dc 71%,#4366e5 100%)}}.hero.is-info{background-color:#209cee;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.tag),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:rgba(255,255,255,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-info .navbar-menu{background-color:#209cee}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:rgba(255,255,255,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#118fe4;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#209cee}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#04a6d7 0,#209cee 71%,#3287f5 100%)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#04a6d7 0,#209cee 71%,#3287f5 100%)}}.hero.is-success{background-color:#23d160;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.tag),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:rgba(255,255,255,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-success .navbar-menu{background-color:#23d160}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:rgba(255,255,255,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#20bc56;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#23d160}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#12af2f 0,#23d160 71%,#2ce28a 100%)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#12af2f 0,#23d160 71%,#2ce28a 100%)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1023px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffaf24 0,#ffdd57 71%,#fffa70 100%)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffaf24 0,#ffdd57 71%,#fffa70 100%)}}.hero.is-danger{background-color:#ff3860;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:rgba(255,255,255,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-danger .navbar-menu{background-color:#ff3860}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:rgba(255,255,255,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#ff3860}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#ff0561 0,#ff3860 71%,#ff5257 100%)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ff0561 0,#ff3860 71%,#ff5257 100%)}}.hero.is-small .hero-body{padding-bottom:1.5rem;padding-top:1.5rem}@media screen and (min-width:769px),print{.hero.is-medium .hero-body{padding-bottom:9rem;padding-top:9rem}}@media screen and (min-width:769px),print{.hero.is-large .hero-body{padding-bottom:18rem;padding-top:18rem}}.hero.is-fullheight .hero-body,.hero.is-halfheight .hero-body{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-halfheight .hero-body>.container{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{bottom:0;left:0;position:absolute;right:0;top:0;overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:-webkit-box;display:-ms-flexbox;display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media screen and (min-width:769px),print{.hero-buttons{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{-webkit-box-flex:0;-ms-flex-positive:0;flex-grow:0;-ms-flex-negative:0;flex-shrink:0}.hero-body{-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-ms-flex-negative:0;flex-shrink:0;padding:3rem 1.5rem}.section{padding:3rem 1.5rem}@media screen and (min-width:1024px){.section.is-medium{padding:9rem 1.5rem}.section.is-large{padding:18rem 1.5rem}}.footer{background-color:#f5f5f5;padding:3rem 1.5rem 6rem}
+/*# sourceMappingURL=bulma.min.css.map */
\ No newline at end of file
diff --git a/smartthings-plugin/plugin/css/helper.css b/smartthings-plugin/plugin/css/helper.css
new file mode 100644 (file)
index 0000000..c016278
--- /dev/null
@@ -0,0 +1,324 @@
+.margin-left-s      {margin-left: 10px !important;}
+.margin-left-m      {margin-left: 20px !important;}
+.margin-left-l      {margin-left: 30px !important;}
+.margin-left-xl     {margin-left: 40px !important;}
+.margin-left-xxl    {margin-left: 50px !important;}
+
+.margin-top-s      {margin-top: 10px !important;}
+
+.margin-top-m      {margin-top: 20px !important;}
+.margin-top-l      {margin-top: 30px !important;}
+.margin-top-xl     {margin-top: 40px !important;}
+.margin-top-xxl    {margin-top: 50px !important;}
+
+.margin-bottom-s      {margin-bottom: 10px !important;}
+.margin-bottom-m      {margin-bottom: 20px !important;}
+.margin-bottom-l      {margin-bottom: 30px !important;}
+.margin-bottom-xl     {margin-bottom: 40px !important;}
+.margin-bottom-xxl    {margin-bottom: 50px !important;}
+
+.margin-right-s      {margin-right: 10px !important;}
+.margin-right-m      {margin-right: 20px !important;}
+.margin-right-l      {margin-right: 30px !important;}
+.margin-right-xl     {margin-right: 40px !important;}
+.margin-right-xxl    {margin-right: 50px !important;}
+
+.padding-left-s      {padding-left: 10px !important;}
+.padding-left-m      {padding-left: 20px !important;}
+.padding-left-l      {padding-left: 30px !important;}
+.padding-left-xl     {padding-left: 40px !important;}
+.padding-left-xxl    {padding-left: 50px !important;}
+
+.padding-top-s      {padding-top: 10px !important;}
+.padding-top-m      {padding-top: 20px !important;}
+.padding-top-l      {padding-top: 30px !important;}
+.padding-top-xl     {padding-top: 40px !important;}
+.padding-top-xxl    {padding-top: 50px !important;}
+
+.padding-bottom-s      {padding-bottom: 10px !important;}
+.padding-bottom-m      {padding-bottom: 20px !important;}
+.padding-bottom-l      {padding-bottom: 30px !important;}
+.padding-bottom-xl     {padding-bottom: 40px !important;}
+.padding-bottom-xxl    {padding-bottom: 50px !important;}
+
+.padding-right-s      {padding-right: 10px !important;}
+.padding-right-m      {padding-right: 20px !important;}
+.padding-right-l      {padding-right: 30px !important;}
+.padding-right-xl     {padding-right: 40px !important;}
+.padding-right-xxl    {padding-right: 50px !important;}
+
+.round-border       {border-radius: 50% !important;}
+
+hr {
+    display: block;
+    height: 1px;
+    margin: 0 0;
+}
+
+html {
+    min-height: 100%;
+    height: 100%;
+    -webkit-tap-highlight-color:transparent;
+}
+
+body {
+    height: inherit;
+    overflow-y: auto;
+       overflow-x: hidden;
+}
+
+.is-vertical-center {
+    display: flex;
+    align-items: center;
+}
+
+#app-title {
+    text-transform: uppercase;
+}
+
+#camera_controller {
+  width: 270px;
+  height: 270px;
+  background-image: url("../res/controller_bg.png");
+  background-size: 100% 100%;
+}
+
+.mid-div {
+    margin: 20px 0 20px 0;
+}
+
+#motion_area {
+    border-radius: 50%;
+    background-color: #ffffff;
+    background-size: contain;
+    width: 100px;
+    height: 100px;
+    display: inline-block;
+    box-shadow: 0 0 5px grey;
+    /* margin-top: 85px; */
+}
+
+#camera_controller[data-state='auto_mode_on'] > #motion_area[data-state='motion_detected'] {
+    background-color: #209cee !important;
+}
+
+#camera_controller[data-state='auto_mode_on'] > #motion_area[data-state='motion_detected'] p{
+    color: #ffffff !important;
+}
+
+#camera_controller[data-state='auto_mode_on'] > #motion_area[data-state='motion_detected'] p:after{
+    content: 'Motion Detected';
+}
+
+#camera_controller[data-state='auto_mode_on'] > #motion_area[data-state='no_motion'] {
+    background-color: #ffffff !important;
+}
+
+#camera_controller[data-state='auto_mode_on'] > #motion_area[data-state='no_motion'] p{
+    color: #777 !important;
+}
+
+#camera_controller[data-state='auto_mode_on'] > #motion_area[data-state='no_motion'] p:after{
+    content: 'No\aMotion';
+    white-space: pre;
+}
+
+#camera_controller[data-state='auto_mode_off'] #motion_area {
+    background-color: #ffffff !important;
+}
+
+#camera_controller[data-state='auto_mode_off'] p{
+    color: #ccc !important;
+}
+
+#camera_controller[data-state='auto_mode_off'] p:after{
+    content: '';
+}
+
+#motion_area > p {
+    transform: translateY(50%);
+}
+
+.icon-move {
+    height: 85px;
+    width: 85px;
+}
+
+#camera_controller[data-state='auto_mode_on'] > .icon-move {
+    background-color: #ccc;
+}
+
+#camera_controller[data-state='auto_mode_off'] > .icon-move {
+    background-color: #209cee;
+}
+
+#move-left, #move-right {
+    margin-top: 10px;
+}
+
+#move-left {
+    float: left;
+    mask: url("../res/arrow_left.png") no-repeat center;
+    mask-size: 18px 30px;
+    -webkit-mask: url("../res/arrow_left.png") no-repeat center;
+    -webkit-mask-size: 18px 30px;
+}
+
+#move-right {
+    float: right;
+    mask: url("../res/arrow_right.png") no-repeat center;
+    mask-size: 18px 30px;
+    -webkit-mask: url("../res/arrow_right.png") no-repeat center;
+    -webkit-mask-size: 18px 30px;
+}
+
+#move-up {
+    mask: url("../res/arrow_up.png") no-repeat center;
+    mask-size: 30px 18px;
+    -webkit-mask: url("../res/arrow_up.png") no-repeat center;
+    -webkit-mask-size: 30px 18px;
+}
+
+#move-down {
+    mask: url("../res/arrow_down.png") no-repeat center;
+    mask-size: 30px 18px;
+    -webkit-mask: url("../res/arrow_down.png") no-repeat center;
+    -webkit-mask-size: 30px 18px;
+}
+
+#auto_mode[data-state='on'] > #auto_mode_on_text {
+    display: block;
+}
+
+#auto_mode[data-state='on'] > #auto_mode_off_text {
+    display: none;
+}
+
+#auto_mode[data-state='off'] > #auto_mode_on_text {
+    display: none;
+}
+
+#auto_mode[data-state='off'] > #auto_mode_off_text {
+    display: block;
+}
+
+#mode_name {
+    width: 200px;
+}
+
+/* slider for IE */
+input[type=range].slider.is-info::-ms-track{
+    background:#209cee!important;
+    height: 2px;
+    border-color: transparent;
+    color: transparent;
+    background-color: transparent;
+}
+
+/* slider for mozilla */
+input[type=range].slider.is-info::-moz-range-track {
+  position:relative;
+  width: 100%;
+  height: 6px;
+  background: #ccc;
+}
+
+input[type=range].slider.is-info::-moz-range-progress {
+  position:relative;
+  width: 100%;
+  height: 6px;
+  background: #209cee;
+}
+
+/* slider for chrome */
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+    input[type='range'].slider.is-info {
+      overflow: hidden;
+      width: 100%;
+      -webkit-appearance: none;
+    }
+
+    input[type='range'].slider.is-info::-webkit-slider-runnable-track {
+      height: 10px;
+      -webkit-appearance: none;
+      color: #13bba4;
+      margin-top: -1px;
+    }
+
+    input[type='range'].slider.is-info::-webkit-slider-thumb {
+      box-shadow: 1080px 0 0 1080px #ccc;
+    }
+}
+
+@media only screen and (min-height: 600px) {
+       html {
+         min-width: 100%;
+         min-height: 100%;
+         width: 100%;
+         height: 100%;
+       }
+}
+
+.leftSide {
+    float: left;
+    width: 100%;
+    background-color: #fafafa;
+}
+
+.rightSide {
+    float: left;
+    width: 100%;
+}
+
+@media only screen and (orientation: landscape) and (min-height: 360px){
+    .leftSide {
+        width: 50%;
+        height: inherit;
+    }
+    .rightSide {
+        width: 50%;
+    }
+}
+
+@media only screen and (min-width: 768px) {
+    .leftSide {
+        height: auto;
+    }
+}
+
+.loader {
+    border: 3px solid #f3f3f3;
+    border-radius: 100%;
+    border-top: 3px solid #3498db;
+    width: 40px;
+    height: 40px;
+    -webkit-animation: spin 2s linear infinite; /* Safari */
+    animation: spin 2s linear infinite;
+}
+
+/* Safari */
+@-webkit-keyframes spin {
+    0% { -webkit-transform: rotate(0deg); }
+    100% { -webkit-transform: rotate(360deg); }
+}
+
+@keyframes spin {
+    0% { transform: rotate(0deg); }
+    100% { transform: rotate(360deg); }
+}
+
+.shadow-up {
+    filter: drop-shadow(0px -10px 5px #cccccc);
+ }
+
+ .shadow-down {
+    filter: drop-shadow(0px 10px 5px #cccccc);
+ }
+
+ .shadow-right {
+    filter: drop-shadow(10px 0px 5px #cccccc);
+ }
+
+ .shadow-left {
+    filter: drop-shadow(-10px 0px 5px #cccccc);
+ }
\ No newline at end of file
diff --git a/smartthings-plugin/plugin/icon.png b/smartthings-plugin/plugin/icon.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/smartthings-plugin/plugin/index.html b/smartthings-plugin/plugin/index.html
new file mode 100644 (file)
index 0000000..05144cc
--- /dev/null
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <!-- Stylesheet Template Bulma IO-->
+               <meta charset="utf-8">
+               <meta name="viewport" content="width=device-width, initial-scale=1">
+               <meta http-equiv="X-UA-Compatible" content="ie=edge">
+               <title>Smart Surveillance Camera</title>
+               <link rel="stylesheet" href="css/bulma.min.css">
+               <link rel="stylesheet" href="css/bulma-switch.min.css">
+               <link rel="stylesheet" href="css/helper.css">
+       </head>
+
+       <body>
+               <!-- Action bar-->
+               <div class="columns is-marginless is-mobile" style="background-color:#fafafa;">
+                       <div class="column is-narrow">
+                               <figure class="image is-24x24 margin-left-s" onclick="backAction()">
+                                       <img src="res/board_ic_arrow_left.png">
+                               </figure>
+                       </div>
+               </div>
+
+               <!-- Left -->
+               <div class="dividecolumn leftSide padding-bottom-l border-right" style="background-color:#fafafa;">
+
+                       <!-- Header-->
+                       <div id="app-title" class="column is-mobile is-paddingless margin-left-l margin-top-s">
+                               <div class="has-text-weight-bold has-text-grey-light is-size-7">Tizen IoT</div>
+                               <div class="has-text-weight-bold">Smart Surveillance Camera</div>
+                       </div>
+                       
+                       <div class="columns is-mobile is-centered margin-top-m" align="center">
+                               <div class="column is-narrow">
+                                       <div id="camera_controller" data-state="auto_mode_on">
+                                               <div id="move-up" class="icon-move" onclick="onMoveUpBtnClicked()"></div>
+                                               <div id="move-left" class="icon-move" onclick="onMoveLeftBtnClicked()"></div>
+                                               <div id="motion_area" align="center" data-state="no_motion">
+                                                       <p class="is-size-10 has-text-weight-bold"></p>
+                                               </div>
+                                               <div id="move-right" class="icon-move" onclick="onMoveRightBtnClicked()"></div>
+                                               <div id="move-down" class="icon-move" onclick="onMoveDownBtnClicked()"></div>
+                                               </span>
+                                       </div>
+                               </div>
+                       </div>
+
+               </div>
+
+               <!-- Right -->
+               <div class="dividecolumn rightSide">
+                       <hr class="is-hidden-tablet" />
+                       <!-- Auto Mode Option -->
+                       <div class="columns is-mobile is-marginless margin-left-s">
+                               <div id="auto_mode" data-state="on" class="column">
+                                       <p id="mode_name">Auto Control Mode</p>
+                                       <p class="is-size-7 has-text-info" id="auto_mode_on_text">On</p>
+                                       <p class="is-size-7 has-text-grey-light" id="auto_mode_off_text">Off</p>
+                               </div>
+                               <div class="field column margin-top-s">
+                                       <input type="checkbox" id="auto_mode_check" name="auto_mode_check" class="switch is-rounded is-info" checked="checked" onclick="onModeBtnClicked()">
+                                       <label class="is-pulled-right" for="auto_mode_check"></label>
+                               </div>
+                       </div>
+                       <hr/>
+               </div>
+       </body>
+
+       <!-- SAMSUNG CONNECT API -->
+       <script type="text/javascript" src="lib/SCPluginApi.js"></script>
+       <script type="text/javascript" src="js/capability_servoMotor.js"></script>
+       <script type="text/javascript" src="js/capability_motionSensor.js"></script>
+       <script type="text/javascript" src="js/capability_modeSwitch.js"></script>
+       <script type="text/javascript" src="js/index.js"></script>
+</html>
diff --git a/smartthings-plugin/plugin/js/capability_modeSwitch.js b/smartthings-plugin/plugin/js/capability_modeSwitch.js
new file mode 100644 (file)
index 0000000..d713160
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015 - 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var capabilityModeSwitch = {
+       'href' : {
+               switch: "/capability/switch/main/0"
+       },
+       'controlMode' : "on",
+
+       'update' : function() {
+               ocfDevice.getRemoteRepresentation(this.href.switch, this.onRepresentCallback);
+       },
+
+       'onRepresentCallback' : function(result, deviceHandle, uri, rcsJsonString) {
+               scplugin.log.debug(className, arguments.callee.name, result);
+               scplugin.log.debug(className, arguments.callee.name, uri);
+
+               if (result == "OCF_OK" || result == "OCF_RESOURCE_CHANGED" || result == "OCF_RES_ALREADY_SUBSCRIBED") {
+                       capabilityModeSwitch.controlMode = rcsJsonString["power"];
+               }
+
+               if (capabilityModeSwitch.controlMode == "off") {
+                       document.getElementById("auto_mode").setAttribute('data-state', 'on');
+                       document.getElementById("camera_controller").setAttribute('data-state', 'auto_mode_on');
+                       document.getElementById("auto_mode_check").checked = true;
+               } else {
+                       document.getElementById("auto_mode").setAttribute('data-state', 'off');
+                       document.getElementById("camera_controller").setAttribute('data-state', 'auto_mode_off');
+                       document.getElementById("auto_mode_check").checked = false;
+               }
+       },
+
+       'set' : function(state) {
+               scplugin.log.debug(className, arguments.callee.name, "power : " + state);
+               var setRcsJson = {};
+               setRcsJson["power"] = state;
+               ocfDevice.setRemoteRepresentation(this.href.switch, setRcsJson, this.onRepresentCallback);
+       },
+
+       'isControllable': function() {
+               if (this.controlMode == 'on') {
+                       return true;
+               } else {
+                       return false;
+               }
+       },
+
+       'modeToggle' : function() {
+               if (this.controlMode == "on") {
+                       this.set("off");
+               } else {
+                       this.set("on");
+               }
+       }
+}
diff --git a/smartthings-plugin/plugin/js/capability_motionSensor.js b/smartthings-plugin/plugin/js/capability_motionSensor.js
new file mode 100644 (file)
index 0000000..3cd7f56
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var capabilityMotionSensor = {
+       'href' : {
+               motion: "/capability/motionSensor/main/0"
+       },
+
+       'update' : function() {
+               ocfDevice.getRemoteRepresentation(this.href.motion, this.onRepresentCallback);
+       },
+
+       'onRepresentCallback' : function(result, deviceHandle, uri, rcsJsonString) {
+               scplugin.log.debug(className, arguments.callee.name, result);
+               scplugin.log.debug(className, arguments.callee.name, uri);
+
+               if (result == "OCF_OK" || result == "OCF_RESOURCE_CHANGED" || result == "OCF_RES_ALREADY_SUBSCRIBED") {
+                       if(rcsJsonString["value"] === undefined) return;
+
+                       var state = rcsJsonString["value"];
+
+                       if (state)
+                               document.getElementById("motion_area").setAttribute('data-state', 'motion_detected');
+                       else
+                               document.getElementById("motion_area").setAttribute('data-state', 'no_motion');
+               }
+       }
+}
diff --git a/smartthings-plugin/plugin/js/capability_servoMotor.js b/smartthings-plugin/plugin/js/capability_servoMotor.js
new file mode 100644 (file)
index 0000000..40b4f61
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const SERVO_RANGE = {
+       VERTICAL: {
+               MIN: 15,
+               MAX: 65
+       },
+       HORIZONTAL: {
+               MIN: 5,
+               MAX: 65
+       }
+};
+const SERVO_STEP = {
+       VERTICAL: (SERVO_RANGE.VERTICAL.MAX - SERVO_RANGE.VERTICAL.MIN) / 10,
+       HORIZONTAL: (SERVO_RANGE.HORIZONTAL.MAX - SERVO_RANGE.HORIZONTAL.MIN) / 10,
+};
+const CONTROL_TYPE_VERTICAL = "vertical";
+const CONTROL_TYPE_HORIZONTAL = "horizontal";
+
+var capabilityServoMotor = {
+       'href' : {
+               v: "/capability/audioVolume/main/0",
+               h: "/capability/switchLevel/main/0"
+       },
+       'valueV' : 50,
+       'valueH' : 50,
+
+       'update' : function() {
+               for (var item in this.href) {
+                       ocfDevice.getRemoteRepresentation(this.href[item], this.onRepresentCallback);
+               }
+       },
+
+       'onRepresentCallback' : function(result, deviceHandle, uri, rcsJsonString) {
+               scplugin.log.debug(className, arguments.callee.name, result);
+               scplugin.log.debug(className, arguments.callee.name, uri);
+
+               if (result == "OCF_OK" || result == "OCF_RESOURCE_CHANGED" || result == "OCF_RES_ALREADY_SUBSCRIBED") {
+                       if (uri == capabilityServoMotor.href.v) {
+                               if(rcsJsonString["volume"] === undefined) return;
+                               capabilityServoMotor.valueV = rcsJsonString["volume"];
+                       } else if (uri == capabilityServoMotor.href.h) {
+                               if(rcsJsonString["dimmingSetting"] === undefined) return;
+                               capabilityServoMotor.valueH = rcsJsonString["dimmingSetting"];
+                       }
+               }
+       },
+
+       'move' : function(type, value) {
+               var setRcsJson = {};
+               var href;
+               scplugin.log.debug(className, arguments.callee.name, "Servo Motor type: " + type, "value: " + value);
+
+               if (type == CONTROL_TYPE_VERTICAL) {
+                       setRcsJson["volume"] = value;
+                       href = this.href.v;
+               } else if (type == CONTROL_TYPE_HORIZONTAL) {
+                       setRcsJson["dimmingSetting"] = value;
+                       href = this.href.h;
+               } else {
+                       scplugin.log.debug(className, arguments.callee.name, "Servo Motor invalid type!");
+               }
+
+               ocfDevice.setRemoteRepresentation(href, setRcsJson, this.onRepresentCallback);
+       },
+
+       'moveDown' : function() {
+               var nextVal = this.valueV + SERVO_STEP.VERTICAL;
+               if (nextVal >= SERVO_RANGE.VERTICAL.MAX)
+                       nextVal = SERVO_RANGE.VERTICAL.MAX;
+
+               this.move(CONTROL_TYPE_VERTICAL, nextVal);
+       },
+
+       'moveUp' : function() {
+               var nextVal = this.valueV - SERVO_STEP.VERTICAL;
+               if (nextVal <= SERVO_RANGE.VERTICAL.MIN)
+                       nextVal = SERVO_RANGE.VERTICAL.MIN;
+
+               this.move(CONTROL_TYPE_VERTICAL, nextVal);
+       },
+       'moveLeft' : function() {
+               var nextVal = this.valueH + SERVO_STEP.HORIZONTAL;
+               if (nextVal >= SERVO_RANGE.HORIZONTAL.MAX)
+                       nextVal = SERVO_RANGE.HORIZONTAL.MAX;
+
+               this.move(CONTROL_TYPE_HORIZONTAL, nextVal);
+       },
+       'moveRight' : function() {
+               var nextVal = this.valueH - SERVO_STEP.HORIZONTAL;
+               if (nextVal <= SERVO_RANGE.HORIZONTAL.MIN)
+                       nextVal = SERVO_RANGE.HORIZONTAL.MIN;
+
+               this.move(CONTROL_TYPE_HORIZONTAL, nextVal);
+       }
+}
diff --git a/smartthings-plugin/plugin/js/index.js b/smartthings-plugin/plugin/js/index.js
new file mode 100644 (file)
index 0000000..0085cb3
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var ocfDevice;
+var capabilities = [capabilityModeSwitch, capabilityMotionSensor, capabilityServoMotor];
+var className = "iot2018";
+
+window.onload = function () {
+       console.log("version : 0.0.1");
+       registerTouchEvents();
+       init();
+};
+
+function init() {
+       console.log("-----------init-----------");
+       scplugin.manager.getOCFDevices(getOCFDeviceCB);
+}
+
+function getOCFDeviceCB(devices) {
+       console.log("getOCFDeviceCB : " + devices.length);
+
+       for (var i in devices) {
+               console.log("deviceHandle: " + devices[i].deviceHandle);
+               console.log("deviceName: " + devices[i].deviceName);
+               console.log("deviceType: " + devices[i].deviceType);
+               console.log("metadata: " + devices[i].metadata);
+       }
+
+       setMainDevice(devices[0]);
+       ocfDevice.subscribe(onRepresentCallback);
+
+       for (var i = 0; i < capabilities.length; i++) {
+               capabilities[i].update();
+       }
+}
+
+function onRepresentCallback(result, deviceHandle, uri, rcsJsonString) {
+       for (var i = 0; i < capabilities.length; i++) {
+               var hrefObj = capabilities[i].href;
+
+               for (var item in hrefObj) {
+                       if (hrefObj[item] == uri)
+                               capabilities[i].onRepresentCallback(result, deviceHandle, uri, rcsJsonString);
+               }
+       }
+}
+
+function registerTouchEvents() {
+       var elements = document.getElementsByClassName("icon-move");
+       // up-left-right-down
+
+       // Move Up
+       elements[0].addEventListener("touchstart", function() {
+               if (!capabilityModeSwitch.isControllable()){
+                       return;
+               }
+               document.getElementById("camera_controller").className = "shadow-up";
+       }, false);
+
+       // Move Left
+       elements[1].addEventListener("touchstart", function() {
+               if (!capabilityModeSwitch.isControllable()){
+                       return;
+               }
+               document.getElementById("camera_controller").className = "shadow-left";
+       }, false);
+
+       // Move Right
+       elements[2].addEventListener("touchstart", function() {
+               if (!capabilityModeSwitch.isControllable()){
+                       return;
+               }
+               document.getElementById("camera_controller").className = "shadow-right";
+       }, false);
+
+       // Move Down
+       elements[3].addEventListener("touchstart", function() {
+               if (!capabilityModeSwitch.isControllable()){
+                       return;
+               }
+               document.getElementById("camera_controller").className = "shadow-down";
+       }, false);
+
+       for (var i = 0; i < elements.length; i++){
+               elements[i].addEventListener("touchend", function() {
+                       document.getElementById("camera_controller").className = "";
+               }, false);
+       }
+}
+
+function setMainDevice(device) {
+       scplugin.log.debug(className, arguments.callee.name, "set ocf device : " + device.deviceName);
+       ocfDevice = device;
+}
+
+function backAction() {
+       scplugin.manager.close();
+}
+
+function onModeBtnClicked() {
+       capabilityModeSwitch.modeToggle();
+}
+
+function onMoveUpBtnClicked() {
+       if (!capabilityModeSwitch.isControllable()){
+               return;
+       }
+       capabilityServoMotor.moveUp();
+}
+
+function onMoveDownBtnClicked() {
+       if (!capabilityModeSwitch.isControllable()){
+               return;
+       }
+       capabilityServoMotor.moveDown();
+}
+
+function onMoveLeftBtnClicked() {
+       if (!capabilityModeSwitch.isControllable()){
+               return;
+       }
+       capabilityServoMotor.moveLeft();
+}
+
+function onMoveRightBtnClicked() {
+       if (!capabilityModeSwitch.isControllable()){
+               return;
+       }
+       capabilityServoMotor.moveRight();
+}
diff --git a/smartthings-plugin/plugin/manifest.xml b/smartthings-plugin/plugin/manifest.xml
new file mode 100644 (file)
index 0000000..b276531
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+  <manifest xmlns="http://samsung.com/ns/plugin"
+      manifestVersionCode="0"
+      package="iot2018"
+      versionCode="1"
+      versionName="0.0.1"
+      minPluginSdkVersion="1.0.0">
+
+    <plugin label="default.plugin" icon="icon.png" >
+       <device type="none"
+         subType="none"
+         manufacturer="default.manufacturer"
+         manufacturerID="default.manufacturer.id" />
+    </plugin>
+    <privilegeLevel level="public"/>
+</manifest>
diff --git a/smartthings-plugin/plugin/res/arrow_down.png b/smartthings-plugin/plugin/res/arrow_down.png
new file mode 100644 (file)
index 0000000..0243267
Binary files /dev/null and b/smartthings-plugin/plugin/res/arrow_down.png differ
diff --git a/smartthings-plugin/plugin/res/arrow_left.png b/smartthings-plugin/plugin/res/arrow_left.png
new file mode 100644 (file)
index 0000000..d285160
Binary files /dev/null and b/smartthings-plugin/plugin/res/arrow_left.png differ
diff --git a/smartthings-plugin/plugin/res/arrow_right.png b/smartthings-plugin/plugin/res/arrow_right.png
new file mode 100644 (file)
index 0000000..8c3c55a
Binary files /dev/null and b/smartthings-plugin/plugin/res/arrow_right.png differ
diff --git a/smartthings-plugin/plugin/res/arrow_up.png b/smartthings-plugin/plugin/res/arrow_up.png
new file mode 100644 (file)
index 0000000..123ca02
Binary files /dev/null and b/smartthings-plugin/plugin/res/arrow_up.png differ
diff --git a/smartthings-plugin/plugin/res/board_ic_arrow_left.png b/smartthings-plugin/plugin/res/board_ic_arrow_left.png
new file mode 100644 (file)
index 0000000..4c6118e
Binary files /dev/null and b/smartthings-plugin/plugin/res/board_ic_arrow_left.png differ
diff --git a/smartthings-plugin/plugin/res/controller_bg.png b/smartthings-plugin/plugin/res/controller_bg.png
new file mode 100644 (file)
index 0000000..abd0c2e
Binary files /dev/null and b/smartthings-plugin/plugin/res/controller_bg.png differ
diff --git a/smartthings-plugin/plugin/webfonts/fa-solid-900.ttf b/smartthings-plugin/plugin/webfonts/fa-solid-900.ttf
new file mode 100644 (file)
index 0000000..618136a
Binary files /dev/null and b/smartthings-plugin/plugin/webfonts/fa-solid-900.ttf differ
diff --git a/smartthings-plugin/plugin/webfonts/fa-solid-900.woff b/smartthings-plugin/plugin/webfonts/fa-solid-900.woff
new file mode 100644 (file)
index 0000000..af7c4f7
Binary files /dev/null and b/smartthings-plugin/plugin/webfonts/fa-solid-900.woff differ
diff --git a/smartthings-plugin/plugin/webfonts/fa-solid-900.woff2 b/smartthings-plugin/plugin/webfonts/fa-solid-900.woff2
new file mode 100644 (file)
index 0000000..f025a00
Binary files /dev/null and b/smartthings-plugin/plugin/webfonts/fa-solid-900.woff2 differ
diff --git a/smartthings-plugin/prod-catalog.json b/smartthings-plugin/prod-catalog.json
new file mode 100644 (file)
index 0000000..3038d30
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "modelCd": "YOUR_DEVICE_NAME",
+  "easysetupId": "001"
+}
\ No newline at end of file
diff --git a/src/controller.c b/src/controller.c
new file mode 100644 (file)
index 0000000..e98435f
--- /dev/null
@@ -0,0 +1,678 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+#include <Ecore.h>
+#include <tizen.h>
+#include <service_app.h>
+#include <camera.h>
+#include <mv_common.h>
+#include <pthread.h>
+#include "controller.h"
+#include "controller_mv.h"
+#include "controller_image.h"
+#include "log.h"
+#include "resource_camera.h"
+#include "switch.h"
+#include "servo-h.h"
+#include "servo-v.h"
+#include "servo-type.h"
+#include "motion.h"
+#include "st_thing_master.h"
+#include "st_thing_resource.h"
+
+#define CAMERA_MOVE_INTERVAL_MS 450
+#define THRESHOLD_EVENT_COUNT 2
+#define VALID_EVENT_INTERVAL_MS 200
+
+#define IMAGE_FILE_PREFIX "CAM_"
+#define EVENT_INTERVAL_SECOND 0.5f
+
+#define TEMP_IMAGE_FILENAME "/tmp/tmp.jpg"
+#define LATEST_IMAGE_FILENAME "/tmp/latest.jpg"
+
+// #define TEST_SERVO_MOTER_CAL 1
+// #define TEST_DEBUG_MODE 1
+// #define ENABLE_SMARTTHINGS
+#define APP_CALLBACK_KEY "controller"
+
+typedef struct app_data_s {
+       long long int last_moved_time;
+       long long int last_valid_event_time;
+       double current_servo_x;
+       double current_servo_y;
+       int motion_state;
+
+       int vision_result_x_sum;
+       int vision_result_y_sum;
+
+       int valid_event_count;
+
+       unsigned int latest_image_width;
+       unsigned int latest_image_height;
+       char *latest_image_info;
+       int latest_image_type; // 0: 카메라 이동 시간의 이미지, 1: 유효 이벤트 숫자 아래의 이미지, 2: 유효한 동작 이미지
+
+       unsigned char *latest_image_buffer;
+
+       Ecore_Thread *image_writter_thread;
+       pthread_mutex_t mutex;
+} app_data;
+
+static long long int __get_monotonic_ms(void)
+{
+       long long int ret_time = 0;
+       struct timespec time_s;
+
+       if (0 == clock_gettime(CLOCK_MONOTONIC, &time_s))
+               ret_time = time_s.tv_sec* 1000 + time_s.tv_nsec / 1000000;
+       else
+               _E("Failed to get time");
+
+       return ret_time;
+}
+
+static mv_colorspace_e __convert_colorspace_from_cam_to_mv(camera_pixel_format_e format)
+{
+       mv_colorspace_e colorspace = MEDIA_VISION_COLORSPACE_INVALID;
+       switch (format) {
+       case CAMERA_PIXEL_FORMAT_NV12:
+               colorspace = MEDIA_VISION_COLORSPACE_NV12;
+               break;
+       case CAMERA_PIXEL_FORMAT_NV21:
+               colorspace = MEDIA_VISION_COLORSPACE_NV21;
+               break;
+       case CAMERA_PIXEL_FORMAT_YUYV:
+               colorspace = MEDIA_VISION_COLORSPACE_YUYV;
+               break;
+       case CAMERA_PIXEL_FORMAT_UYVY:
+               colorspace = MEDIA_VISION_COLORSPACE_UYVY;
+               break;
+       case CAMERA_PIXEL_FORMAT_422P:
+               colorspace = MEDIA_VISION_COLORSPACE_422P;
+               break;
+       case CAMERA_PIXEL_FORMAT_I420:
+               colorspace = MEDIA_VISION_COLORSPACE_I420;
+               break;
+       case CAMERA_PIXEL_FORMAT_YV12:
+               colorspace = MEDIA_VISION_COLORSPACE_YV12;
+               break;
+       case CAMERA_PIXEL_FORMAT_RGB565:
+               colorspace = MEDIA_VISION_COLORSPACE_RGB565;
+               break;
+       case CAMERA_PIXEL_FORMAT_RGB888:
+               colorspace = MEDIA_VISION_COLORSPACE_RGB888;
+               break;
+       case CAMERA_PIXEL_FORMAT_RGBA:
+               colorspace = MEDIA_VISION_COLORSPACE_RGBA;
+               break;
+       case CAMERA_PIXEL_FORMAT_NV12T:
+       case CAMERA_PIXEL_FORMAT_NV16:
+       case CAMERA_PIXEL_FORMAT_ARGB:
+       case CAMERA_PIXEL_FORMAT_JPEG:
+       case CAMERA_PIXEL_FORMAT_H264:
+       case CAMERA_PIXEL_FORMAT_INVALID:
+       default :
+               colorspace = MEDIA_VISION_COLORSPACE_INVALID;
+               _E("unsupported format : %d", format);
+               break;
+       }
+
+       return colorspace;
+}
+
+static void __thread_write_image_file(void *data, Ecore_Thread *th)
+{
+       app_data *ad = (app_data *)data;
+       unsigned int width = 0;
+       unsigned int height = 0;
+       unsigned char *buffer = 0;
+       char *image_info = NULL;
+       int ret = 0;
+
+       pthread_mutex_lock(&ad->mutex);
+       width = ad->latest_image_width;
+       height = ad->latest_image_height;
+       buffer = ad->latest_image_buffer;
+       ad->latest_image_buffer = NULL;
+       if (ad->latest_image_info) {
+               image_info = ad->latest_image_info;
+               ad->latest_image_info = NULL;
+       } else {
+               image_info = strdup("00");
+       }
+       pthread_mutex_unlock(&ad->mutex);
+
+#ifdef TEST_DEBUG_MODE
+       char filename[PATH_MAX] = {'\0', };
+       static long long int captured_time = 0;
+       long long int now = __get_monotonic_ms();
+       int servo_x = 0;
+       int servo_y = 0;
+       int latest_image_type = 0;
+
+       char *data_path = NULL;
+       data_path = app_get_data_path();
+
+       pthread_mutex_lock(&ad->mutex);
+       servo_x = (int)ad->current_servo_x;
+       servo_y = (int)ad->current_servo_y;
+       latest_image_type = ad->latest_image_type;
+       pthread_mutex_unlock(&ad->mutex);
+
+       snprintf(filename, PATH_MAX, "%s%s%lld_H_%d_V_%d_%lld_%d.jpg",
+               data_path,
+               IMAGE_FILE_PREFIX,
+               __get_monotonic_ms(),
+               servo_x,
+               servo_y,
+               __get_monotonic_ms() - captured_time,
+               latest_image_type);
+
+       free(data_path);
+       data_path = NULL;
+
+       ret = controller_image_save_image_file(filename, width, height, buffer, image_info, strlen(image_info));
+       if (ret)
+               _E("failed to save image file");
+
+       captured_time = __get_monotonic_ms();
+#else
+       ret = controller_image_save_image_file(TEMP_IMAGE_FILENAME, width, height, buffer, image_info, strlen(image_info));
+       if (ret) {
+               _E("failed to save image file");
+       } else {
+               ret = rename(TEMP_IMAGE_FILENAME, LATEST_IMAGE_FILENAME);
+               if (ret != 0 )
+                       _E("Rename fail");
+       }
+#endif
+       free(image_info);
+       free(buffer);
+}
+
+static void __thread_end_cb(void *data, Ecore_Thread *th)
+{
+       app_data *ad = (app_data *)data;
+
+       // _D("Normal termination for thread %p.\n", th);
+       pthread_mutex_lock(&ad->mutex);
+       ad->image_writter_thread = NULL;
+       pthread_mutex_unlock(&ad->mutex);
+}
+
+static void __thread_cancel_cb(void *data, Ecore_Thread *th)
+{
+       app_data *ad = (app_data *)data;
+       unsigned char *buffer = NULL;
+
+       _E("Thread %p got cancelled.\n", th);
+       pthread_mutex_lock(&ad->mutex);
+       buffer = ad->latest_image_buffer;
+       ad->latest_image_buffer = NULL;
+       ad->image_writter_thread = NULL;
+       pthread_mutex_unlock(&ad->mutex);
+
+       free(buffer);
+}
+
+static void __copy_image_buffer(image_buffer_data_s *image_buffer, app_data *ad)
+{
+       unsigned char *buffer = NULL;
+
+       pthread_mutex_lock(&ad->mutex);
+       ad->latest_image_height = image_buffer->image_height;
+       ad->latest_image_width = image_buffer->image_width;
+
+       buffer = ad->latest_image_buffer;
+       ad->latest_image_buffer = image_buffer->buffer;
+       pthread_mutex_unlock(&ad->mutex);
+
+       free(buffer);
+}
+
+static void __preview_image_buffer_created_cb(void *data)
+{
+       image_buffer_data_s *image_buffer = data;
+       app_data *ad = (app_data *)image_buffer->user_data;
+       mv_source_h source = NULL;
+       mv_colorspace_e image_colorspace = MEDIA_VISION_COLORSPACE_INVALID;
+       switch_state_e switch_state = SWITCH_STATE_OFF;
+       char *info = NULL;
+
+       ret_if(!image_buffer);
+       ret_if(!ad);
+
+#ifdef TEST_DEBUG_MODE
+       static long long int last_time = 0;
+       static int seq = 0;
+       long long int now = __get_monotonic_ms();
+       seq++;
+       _D("BUFFER seq[%d] interval[%lld]", seq, now - last_time);
+       last_time = now;
+#endif
+
+       image_colorspace = __convert_colorspace_from_cam_to_mv(image_buffer->format);
+       goto_if(image_colorspace == MEDIA_VISION_COLORSPACE_INVALID, FREE_ALL_BUFFER);
+
+       __copy_image_buffer(image_buffer, ad);
+
+       switch_state_get(&switch_state);
+       if (switch_state == SWITCH_STATE_OFF) { /* SWITCH_STATE_OFF means automatic mode */
+               source = controller_mv_create_source(image_buffer->buffer,
+                                       image_buffer->buffer_size, image_buffer->image_width,
+                                       image_buffer->image_height, image_colorspace);
+       }
+
+       pthread_mutex_lock(&ad->mutex);
+       info = ad->latest_image_info;
+       ad->latest_image_info = NULL;
+       pthread_mutex_unlock(&ad->mutex);
+       free(info);
+
+       if (source)
+               controller_mv_push_source(source);
+
+#ifdef TEST_DEBUG_MODE
+       _D("Vision need %lldms", __get_monotonic_ms() - now);
+#endif
+       free(image_buffer);
+
+       motion_state_set(ad->motion_state, APP_CALLBACK_KEY);
+       ad->motion_state = 0;
+
+       pthread_mutex_lock(&ad->mutex);
+       if (!ad->image_writter_thread) {
+               ad->image_writter_thread = ecore_thread_run(__thread_write_image_file, __thread_end_cb, __thread_cancel_cb, ad);
+       } else {
+               _E("Thread is running NOW");
+       }
+       pthread_mutex_unlock(&ad->mutex);
+
+       return;
+
+FREE_ALL_BUFFER:
+       free(image_buffer->buffer);
+       free(image_buffer);
+}
+
+// x, y -10 ~ 10 offset
+static void __move_camera(int x, int y, void *user_data)
+{
+       app_data *ad = (app_data *)user_data;
+       ret_if(!ad);
+
+       if (x > 10) x = 10;
+       if (x < -10) x = -10;
+       if (y > 10) y = 10;
+       if (y < -10) y = -10;
+
+       x *= -1; // 카메라는 좌우 반전!!
+       double calculated_x = ad->current_servo_x + x * SERVO_MOTOR_HORIZONTAL_STEP;
+       double calculated_y = ad->current_servo_y + y * SERVO_MOTOR_VERTICAL_STEP;
+
+       if (calculated_x > SERVO_MOTOR_HORIZONTAL_MAX)
+               calculated_x = SERVO_MOTOR_HORIZONTAL_MAX;
+
+       if (calculated_x < SERVO_MOTOR_HORIZONTAL_MIN)
+               calculated_x = SERVO_MOTOR_HORIZONTAL_MIN;
+
+       if (calculated_y > SERVO_MOTOR_VERTICAL_MAX)
+               calculated_y = SERVO_MOTOR_VERTICAL_MAX;
+
+       if (calculated_y < SERVO_MOTOR_VERTICAL_MIN)
+               calculated_y = SERVO_MOTOR_VERTICAL_MIN;
+
+       ad->current_servo_x = calculated_x;
+       ad->current_servo_y = calculated_y;
+
+       servo_h_state_set(calculated_x, APP_CALLBACK_KEY);
+       servo_v_state_set(calculated_y, APP_CALLBACK_KEY);
+
+       return;
+}
+
+static void __set_result_info(int result[], int result_count, app_data *ad, int image_result_type)
+{
+       char image_info[IMAGE_INFO_MAX + 1] = {'\0', };
+       char *current_position;
+       int current_index = 0;
+       int string_count = 0;
+       int i = 0;
+       char *latest_image_info = NULL;
+       char *info = NULL;
+
+       current_position = image_info;
+
+       current_position += snprintf(current_position, IMAGE_INFO_MAX, "%02d", image_result_type);
+       string_count += 2;
+
+       current_position += snprintf(current_position, IMAGE_INFO_MAX - string_count, "%02d", result_count);
+       string_count += 2;
+
+       for (i = 0; i < result_count; i++) {
+               current_index = i * 4;
+#ifdef TEST_DEBUG_MODE
+               _D("RESULT: [%02d] [%02d] [%02d] [%02d]", result[i], result[i + 1], result[i + 2], result[i + 3]);
+#endif
+               if (IMAGE_INFO_MAX - string_count < 0)
+                       break;
+
+               current_position += snprintf(current_position, IMAGE_INFO_MAX - string_count, "%02d%02d%02d%02d"
+                       , result[current_index], result[current_index + 1], result[current_index + 2], result[current_index + 3]);
+               string_count += 8;
+       }
+
+#ifdef TEST_DEBUG_MODE
+       _D("RESULT: Count [%02d] String [%s]", result_count, image_info);
+#endif
+       latest_image_info = strdup(image_info);
+       pthread_mutex_lock(&ad->mutex);
+       info = ad->latest_image_info;
+       ad->latest_image_info = latest_image_info;
+       pthread_mutex_unlock(&ad->mutex);
+       free(info);
+}
+
+static void __mv_detection_event_cb(int horizontal, int vertical, int result[], int result_count, void *user_data)
+{
+       app_data *ad = (app_data *)user_data;
+       long long int now = __get_monotonic_ms();
+
+       ad->motion_state = 1;
+
+       if (now < ad->last_moved_time + CAMERA_MOVE_INTERVAL_MS) {
+               // _D("@@@ CAMERA MOVE TIME @@@");
+               ad->valid_event_count = 0;
+               pthread_mutex_lock(&ad->mutex);
+               ad->latest_image_type = 0;
+               pthread_mutex_unlock(&ad->mutex);
+               __set_result_info(result, result_count, ad, 0);
+               return;
+       }
+
+       if (now < ad->last_valid_event_time + VALID_EVENT_INTERVAL_MS) {
+               ad->valid_event_count++;
+       } else {
+               ad->valid_event_count = 1;
+       }
+
+       ad->last_valid_event_time = now;
+
+       if (ad->valid_event_count < THRESHOLD_EVENT_COUNT) {
+               ad->vision_result_x_sum += horizontal;
+               ad->vision_result_y_sum += vertical;
+               pthread_mutex_lock(&ad->mutex);
+               ad->latest_image_type = 1;
+               pthread_mutex_unlock(&ad->mutex);
+               __set_result_info(result, result_count, ad, 1);
+               return;
+       }
+
+       ad->valid_event_count = 0;
+       ad->vision_result_x_sum += horizontal;
+       ad->vision_result_y_sum += vertical;
+
+       int x = ad->vision_result_x_sum / THRESHOLD_EVENT_COUNT;
+       int y = ad->vision_result_y_sum / THRESHOLD_EVENT_COUNT;
+
+       x = 10 * x / (IMAGE_WIDTH / 2);
+       y = 10 * y / (IMAGE_HEIGHT / 2);
+
+#ifndef TEST_SERVO_MOTER_CAL
+       __move_camera((int) x, (int) y, ad);
+#endif
+       ad->last_moved_time = now;
+
+       ad->vision_result_x_sum = 0;
+       ad->vision_result_y_sum = 0;
+       pthread_mutex_lock(&ad->mutex);
+       ad->latest_image_type = 2;
+       pthread_mutex_unlock(&ad->mutex);
+
+       __set_result_info(result, result_count, ad, 2);
+}
+
+#ifdef TEST_SERVO_MOTER_CAL
+static Eina_Bool _control_interval_event_cb(void *data)
+{
+       static int h = SERVO_MOTOR_HORIZONTAL_MIN;
+       static int v = SERVO_MOTOR_VERTICAL_MIN;
+
+       v += SERVO_MOTOR_VERTICAL_STEP;
+       if (v > SERVO_MOTOR_VERTICAL_MAX)
+               v = SERVO_MOTOR_VERTICAL_MIN;
+
+       h += SERVO_MOTOR_HORIZONTAL_STEP;
+       if (h > SERVO_MOTOR_HORIZONTAL_MAX)
+               h = SERVO_MOTOR_HORIZONTAL_MIN;
+
+       // servo_h_state_set(h, APP_CALLBACK_KEY);
+       // servo_v_state_set(v, APP_CALLBACK_KEY);
+
+       _D("SERVO V ONLY v[%d] h[%d]", v, h);
+
+       return ECORE_CALLBACK_RENEW;
+}
+#endif
+
+static void __switch_changed(switch_state_e state, void* user_data)
+{
+       app_data *ad = (app_data *)user_data;
+       _D("switch changed to - %d", state);
+       ret_if(!ad);
+
+       /* Move servo motors to initial position */
+       if (state != SWITCH_STATE_ON)
+               return;
+
+       ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
+       ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
+
+       servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
+       servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
+}
+
+static void __servo_v_changed(double value, void* user_data)
+{
+       app_data *ad = (app_data *)user_data;
+       ret_if(!ad);
+
+       _D("servo_v changed to - %lf", value);
+       ad->current_servo_y = value;
+}
+
+static void __servo_h_changed(double value, void* user_data)
+{
+       app_data *ad = (app_data *)user_data;
+       ret_if(!ad);
+
+       _D("servo_h changed to - %lf", value);
+       ad->current_servo_x = value;
+}
+
+static void __device_interfaces_fini(void)
+{
+       switch_finalize();
+       servo_v_finalize();
+       servo_h_finalize();
+       motion_finalize();
+}
+
+static int __device_interfaces_init(app_data *ad)
+{
+       retv_if(!ad, -1);
+
+       if (switch_initialize()) {
+               _E("failed to switch_initialize()");
+               return -1;
+       }
+
+       if (servo_v_initialize()) {
+               _E("failed to switch_initialize()");
+               goto ERROR;
+       }
+
+       if (servo_h_initialize()) {
+               _E("failed to switch_initialize()");
+               goto ERROR;
+       }
+
+       if (motion_initialize()) {
+               _E("failed to motion_initialize()");
+               goto ERROR;
+       }
+
+       switch_state_changed_cb_set(APP_CALLBACK_KEY, __switch_changed, ad);
+       servo_v_state_changed_cb_set(APP_CALLBACK_KEY, __servo_v_changed, ad);
+       servo_h_state_changed_cb_set(APP_CALLBACK_KEY, __servo_h_changed, ad);
+       // motion : only set result value, callback is not needed
+
+       return 0;
+
+ERROR :
+       __device_interfaces_fini();
+       return -1;
+}
+
+static bool service_app_create(void *data)
+{
+       app_data *ad = (app_data *)data;
+
+       controller_image_initialize();
+
+       pthread_mutex_init(&ad->mutex, NULL);
+
+       if (__device_interfaces_init(ad))
+               goto ERROR;
+
+       if (controller_mv_set_movement_detection_event_cb(__mv_detection_event_cb, data) == -1) {
+               _E("Failed to set movement detection event callback");
+               goto ERROR;
+       }
+
+       if (resource_camera_init(__preview_image_buffer_created_cb, ad) == -1) {
+               _E("Failed to init camera");
+               goto ERROR;
+       }
+
+#ifdef TEST_SERVO_MOTER_CAL
+       ecore_timer_add(EVENT_INTERVAL_SECOND, _control_interval_event_cb, NULL);
+#endif
+
+       if (resource_camera_start_preview() == -1) {
+               _E("Failed to start camera preview");
+               goto ERROR;
+       }
+
+#ifdef ENABLE_SMARTTHINGS
+       /* smartthings APIs should be called after camera start preview, they can't wait to start camera */
+       if (st_thing_master_init())
+               goto ERROR;
+
+       if (st_thing_resource_init())
+               goto ERROR;
+#endif /* ENABLE_SMARTTHINGS */
+
+       ad->current_servo_x = SERVO_MOTOR_HORIZONTAL_CENTER;
+       ad->current_servo_y = SERVO_MOTOR_VERTICAL_CENTER;
+
+       servo_h_state_set(ad->current_servo_x, APP_CALLBACK_KEY);
+       servo_v_state_set(ad->current_servo_y, APP_CALLBACK_KEY);
+
+       return true;
+
+ERROR:
+       __device_interfaces_fini();
+
+       resource_camera_close();
+       controller_mv_unset_movement_detection_event_cb();
+       controller_image_finalize();
+
+#ifdef ENABLE_SMARTTHINGS
+       st_thing_master_fini();
+       st_thing_resource_fini();
+#endif /* ENABLE_SMARTTHINGS */
+
+       pthread_mutex_destroy(&ad->mutex);
+
+       return false;
+}
+
+static void service_app_terminate(void *data)
+{
+       app_data *ad = (app_data *)data;
+       Ecore_Thread *thread_id = NULL;
+       unsigned char *buffer = NULL;
+       char *info = NULL;
+       _D("App Terminated - enter");
+
+       resource_camera_close();
+       controller_mv_unset_movement_detection_event_cb();
+
+       pthread_mutex_lock(&ad->mutex);
+       thread_id = ad->image_writter_thread;
+       ad->image_writter_thread = NULL;
+       pthread_mutex_unlock(&ad->mutex);
+
+       if (thread_id)
+               ecore_thread_wait(thread_id, 3.0); // wait for 3 second
+
+       __device_interfaces_fini();
+
+       controller_image_finalize();
+
+#ifdef ENABLE_SMARTTHINGS
+       st_thing_master_fini();
+       st_thing_resource_fini();
+#endif /* ENABLE_SMARTTHINGS */
+
+       pthread_mutex_lock(&ad->mutex);
+       buffer = ad->latest_image_buffer;
+       ad->latest_image_buffer = NULL;
+       info  = ad->latest_image_info;
+       ad->latest_image_info = NULL;
+       pthread_mutex_unlock(&ad->mutex);
+       free(buffer);
+       free(info);
+
+       pthread_mutex_destroy(&ad->mutex);
+       free(ad);
+       _D("App Terminated - leave");
+}
+
+static void service_app_control(app_control_h app_control, void *data)
+{
+       /* APP_CONTROL */
+}
+
+int main(int argc, char* argv[])
+{
+       app_data *ad = NULL;
+       int ret = 0;
+       service_app_lifecycle_callback_s event_callback;
+
+       ad = calloc(1, sizeof(app_data));
+       retv_if(!ad, -1);
+
+       event_callback.create = service_app_create;
+       event_callback.terminate = service_app_terminate;
+       event_callback.app_control = service_app_control;
+
+       ret = service_app_main(argc, argv, &event_callback, ad);
+
+       return ret;
+}
diff --git a/src/controller_image.c b/src/controller_image.c
new file mode 100644 (file)
index 0000000..5e47b70
--- /dev/null
@@ -0,0 +1,141 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <tizen.h>
+#include <image_util.h>
+#include "log.h"
+#include "exif.h"
+
+static image_util_encode_h encode_h = NULL;
+static image_util_decode_h decode_h = NULL;
+
+#define IMAGE_COLORSPACE IMAGE_UTIL_COLORSPACE_I420
+
+void controller_image_initialize(void)
+{
+       int error_code = image_util_encode_create(IMAGE_UTIL_JPEG, &encode_h);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_encode_create [%s]", get_error_message(error_code));
+       }
+
+       error_code = image_util_decode_create(&decode_h);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_decode_create [%s]", get_error_message(error_code));
+       }
+}
+
+void controller_image_finalize(void)
+{
+       int error_code = image_util_encode_destroy(encode_h);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_encode_destroy [%s]", get_error_message(error_code));
+       }
+
+       error_code = image_util_decode_destroy(decode_h);
+    if (error_code != IMAGE_UTIL_ERROR_NONE) {
+        _E("image_util_decode_destroy [%s]", get_error_message(error_code));
+    }
+}
+
+int controller_image_save_image_file(const char *path,
+       unsigned int width, unsigned int height, const unsigned char *buffer,
+       const char *comment, unsigned int comment_len)
+{
+       unsigned char *encoded = NULL;
+       unsigned long long size = 0;
+
+       int error_code = image_util_encode_set_resolution(encode_h, width, height);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_encode_set_resolution [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_encode_set_colorspace(encode_h, IMAGE_COLORSPACE);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_encode_set_colorspace [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_encode_set_quality(encode_h, 90);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_encode_set_quality [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_encode_set_input_buffer(encode_h, buffer);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_encode_set_input_buffer [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_encode_set_output_buffer(encode_h, &encoded);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_encode_set_output_buffer [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_encode_run(encode_h, &size);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_encode_run [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = exif_write_jpg_file_with_comment(path,
+                       encoded, (unsigned int)size, width, height, comment, comment_len);
+
+       free(encoded);
+
+       return error_code;
+}
+
+int controller_image_read_image_file(const char *path,
+       unsigned long *width, unsigned long *height, unsigned char *buffer, unsigned long long *size)
+{
+       int error_code = image_util_decode_set_input_path(decode_h, path);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_decode_set_input_path [%s] [%s]", path, get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_decode_set_output_buffer(decode_h, &buffer);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_decode_set_output_buffer [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_decode_set_colorspace(decode_h, IMAGE_UTIL_COLORSPACE_RGBA8888);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_decode_set_colorspace [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_decode_set_jpeg_downscale(decode_h, IMAGE_UTIL_DOWNSCALE_1_1);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_decode_set_jpeg_downscale [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       error_code = image_util_decode_run(decode_h, width, height, size);
+       if (error_code != IMAGE_UTIL_ERROR_NONE) {
+               _E("image_util_decode_run [%s]", get_error_message(error_code));
+               return -1;
+       }
+
+       return error_code;
+}
diff --git a/src/controller_mv.c b/src/controller_mv.c
new file mode 100644 (file)
index 0000000..4462b47
--- /dev/null
@@ -0,0 +1,258 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <mv_common.h>
+#include <mv_surveillance.h>
+#include "controller.h"
+#include "controller_mv.h"
+#include "log.h"
+
+#define VIDEO_STREAM_ID 0
+#define THRESHOLD_SIZE_REGION 100
+
+struct __mv_data {
+       mv_surveillance_event_trigger_h mv_trigger_handle;
+       movement_detected_cb movement_detected_cb;
+       void *movement_detected_cb_data;
+};
+
+static struct __mv_data *mv_data = NULL;
+
+static const char *__mv_err_to_str(mv_error_e err)
+{
+       const char *err_str;
+       switch (err) {
+       case MEDIA_VISION_ERROR_NONE:
+               err_str = "MEDIA_VISION_ERROR_NONE";
+               break;
+       case MEDIA_VISION_ERROR_NOT_SUPPORTED:
+               err_str = "MEDIA_VISION_ERROR_NOT_SUPPORTED";
+               break;
+       case MEDIA_VISION_ERROR_MSG_TOO_LONG:
+               err_str = "MEDIA_VISION_ERROR_MSG_TOO_LONG";
+               break;
+       case MEDIA_VISION_ERROR_NO_DATA:
+               err_str = "MEDIA_VISION_ERROR_NO_DATA";
+               break;
+       case MEDIA_VISION_ERROR_KEY_NOT_AVAILABLE:
+               err_str = "MEDIA_VISION_ERROR_KEY_NOT_AVAILABLE";
+               break;
+       case MEDIA_VISION_ERROR_OUT_OF_MEMORY:
+               err_str = "MEDIA_VISION_ERROR_OUT_OF_MEMORY";
+               break;
+       case MEDIA_VISION_ERROR_INVALID_PARAMETER:
+               err_str = "MEDIA_VISION_ERROR_INVALID_PARAMETER";
+               break;
+       case MEDIA_VISION_ERROR_INVALID_OPERATION:
+               err_str = "MEDIA_VISION_ERROR_INVALID_OPERATION";
+               break;
+       case MEDIA_VISION_ERROR_PERMISSION_DENIED:
+               err_str = "MEDIA_VISION_ERROR_PERMISSION_DENIED";
+               break;
+       case MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT:
+               err_str = "MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT";
+               break;
+       case MEDIA_VISION_ERROR_INTERNAL:
+               err_str = "MEDIA_VISION_ERROR_INTERNAL";
+               break;
+       case MEDIA_VISION_ERROR_INVALID_DATA:
+               err_str = "MEDIA_VISION_ERROR_INVALID_DATA";
+               break;
+       case MEDIA_VISION_ERROR_INVALID_PATH:
+               err_str = "MEDIA_VISION_ERROR_INVALID_PATH";
+               break;
+       default:
+               err_str = "Unknown Error";
+               break;
+       }
+
+       return err_str;
+}
+
+static void __movement_detected_event_cb(mv_surveillance_event_trigger_h trigger, mv_source_h source, int video_stream_id, mv_surveillance_result_h event_result, void *data)
+{
+       int ret = 0;
+       int horizontal = 0;
+       int vertical = 0;
+       int result[MV_RESULT_LENGTH_MAX] = {0, };
+       int result_count = 0;
+       int result_index = 0;
+       int valid_area_sum = 0;
+       int i;
+       size_t move_regions_num = 0;
+       mv_rectangle_s *regions = NULL;
+
+       ret_if(!trigger);
+       ret_if(!event_result);
+       ret_if(!mv_data);
+
+       ret = mv_surveillance_get_result_value(event_result, MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS, &move_regions_num);
+       retm_if(ret, "failed to mv_surveillance_get_result_value for %s - [%s]", MV_SURVEILLANCE_MOVEMENT_NUMBER_OF_REGIONS, __mv_err_to_str(ret));
+
+       regions = malloc(sizeof(mv_rectangle_s) * move_regions_num);
+       ret = mv_surveillance_get_result_value(event_result, MV_SURVEILLANCE_MOVEMENT_REGIONS, regions);
+       retm_if(ret, "failed to mv_surveillance_get_result_value for %s - [%s]", MV_SURVEILLANCE_MOVEMENT_REGIONS, __mv_err_to_str(ret));
+
+       for (i = 0; i < move_regions_num; i++) {
+               // _D("region[%u] - position[%d x %d], witdh[%d], height[%d]", i, regions[i].point.x, regions[i].point.y, regions[i].width, regions[i].height);
+               // _D("region[%u] - area[%d]", i, regions[i].width * regions[i].height);
+
+               if (regions[i].width * regions[i].height < THRESHOLD_SIZE_REGION || result_count >= MV_RESULT_COUNT_MAX)
+                       continue;
+
+               result[result_index] = regions[i].point.x * 99 / IMAGE_WIDTH;
+               result[result_index + 1] = regions[i].point.y * 99 / IMAGE_HEIGHT;
+               result[result_index + 2] = regions[i].width * 99 / IMAGE_WIDTH;
+               result[result_index + 3] = regions[i].height * 99 / IMAGE_HEIGHT;
+
+               result_count++;
+               result_index = result_count * 4;
+
+               valid_area_sum += regions[i].width * regions[i].height;
+       }
+
+       for (i = 0; i < move_regions_num; i++) {
+               if (regions[i].width * regions[i].height < THRESHOLD_SIZE_REGION)
+                       continue;
+
+               //offset 은 움직임의 중심 좌표가 화면의 중심으로 부터 얼마나 벗어났는지의 값으로 -160 ~ 160, -120 ~ 120 의 값을 갖는다.
+               int x_offset = (regions[i].point.x + regions[i].width / 2) - (IMAGE_WIDTH / 2);
+               int y_offset = (regions[i].point.y + regions[i].height / 2) - (IMAGE_HEIGHT / 2);
+               int area = regions[i].width * regions[i].height;
+
+               // offset 값에 움직임 크기의 상대값(비율)을 곱한 다음, 모두 더해서 최종 offset 값을 구한다.
+               // 최종값의 범위는 offset 값의 범위와 같아야 한다.
+               horizontal += (int) x_offset * area / valid_area_sum;
+               vertical += (int) y_offset * area / valid_area_sum;
+       }
+       free(regions);
+
+       mv_data->movement_detected_cb(horizontal, vertical, result, result_count, mv_data->movement_detected_cb_data);
+}
+
+void controller_mv_push_source(mv_source_h source)
+{
+       int ret = 0;
+       ret_if(!source);
+
+       ret = mv_surveillance_push_source(source, VIDEO_STREAM_ID);
+       if (ret)
+               _E("failed to mv_surveillance_push_source() - [%s]", __mv_err_to_str(ret));
+
+       mv_destroy_source(source);
+}
+
+mv_source_h controller_mv_create_source(
+               unsigned char *buffer, unsigned int size,
+               unsigned int width, unsigned int height, mv_colorspace_e colorspace)
+{
+       mv_source_h source = NULL;
+       int ret = 0;
+
+       retv_if(!buffer, NULL);
+
+       ret = mv_create_source(&source);
+       retvm_if(ret, NULL, "failed to mv_create_source - [%s]", __mv_err_to_str(ret));
+
+       ret = mv_source_fill_by_buffer(source, buffer, size, width, height, colorspace);
+       if (ret) {
+               _E("failed to fill source - %d", ret);
+
+               if (source)
+                       mv_destroy_source(source);
+
+               return NULL;
+       }
+
+       return source;
+}
+
+int controller_mv_set_movement_detection_event_cb(movement_detected_cb movement_detected_cb, void *user_data)
+{
+       int ret = 0;
+       mv_engine_config_h engine_cfg = NULL;
+
+       if (movement_detected_cb == NULL)
+               return -1;
+
+       if (mv_data != NULL) {
+               mv_data->movement_detected_cb = movement_detected_cb;
+               mv_data->movement_detected_cb_data = user_data;
+               return 0;
+       }
+
+       mv_data = malloc(sizeof(struct __mv_data));
+       if (mv_data == NULL) {
+               _E("Failed to allocate media vision data");
+               return -1;
+       }
+       memset(mv_data, 0, sizeof(struct __mv_data));
+
+       ret = mv_create_engine_config(&engine_cfg);
+       if (ret) {
+               _E("failed to subsmv_create_engine_configs() - %s", __mv_err_to_str(ret));
+               goto ERROR;
+       }
+
+       mv_engine_config_set_int_attribute(engine_cfg, MV_SURVEILLANCE_MOVEMENT_DETECTION_THRESHOLD, 50); /* 10 is default value [0 ~ 255] */
+
+       ret = mv_surveillance_event_trigger_create(MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED, &mv_data->mv_trigger_handle);
+       if (ret) {
+               _E("failed to mv_surveillance_event_trigger_create - [%s]", __mv_err_to_str(ret));
+               goto ERROR;
+       }
+
+       ret = mv_surveillance_subscribe_event_trigger(mv_data->mv_trigger_handle, VIDEO_STREAM_ID, engine_cfg, __movement_detected_event_cb, mv_data);
+
+       if (ret) {
+               _E("failed to subscribe %s - %s", MV_SURVEILLANCE_EVENT_TYPE_MOVEMENT_DETECTED, __mv_err_to_str(ret));
+               goto ERROR;
+       }
+
+       if (engine_cfg)
+               mv_destroy_engine_config(engine_cfg);
+
+       mv_data->movement_detected_cb = movement_detected_cb;
+       mv_data->movement_detected_cb_data = user_data;
+
+       return 0;
+
+ERROR:
+       if (engine_cfg)
+               mv_destroy_engine_config(engine_cfg);
+
+       if (mv_data->mv_trigger_handle)
+               mv_surveillance_event_trigger_destroy(mv_data->mv_trigger_handle);
+
+       free(mv_data);
+       mv_data = NULL;
+
+       return -1;
+}
+
+void controller_mv_unset_movement_detection_event_cb(void)
+{
+       if (mv_data == NULL)
+               return;
+
+       if (mv_data->mv_trigger_handle)
+               mv_surveillance_event_trigger_destroy(mv_data->mv_trigger_handle);
+
+       free(mv_data);
+       mv_data = NULL;
+}
diff --git a/src/exif.c b/src/exif.c
new file mode 100644 (file)
index 0000000..c4ce134
--- /dev/null
@@ -0,0 +1,305 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libexif/exif-loader.h>
+#include <libexif/exif-utils.h>
+#include <libexif/exif-data.h>
+#include "log.h"
+
+#define ASCII_COMMENT_HEADER "ASCII\0\0\0"
+// #define CHECK_EXIF_BEFOR_CREATE
+
+static int check_exif_from_data(const unsigned char *img, unsigned int size)
+{
+#ifdef CHECK_EXIF_BEFOR_CREATE
+       ExifLoader *loader = NULL;
+       unsigned char ret = 0;
+       const unsigned char *buf = NULL;
+       unsigned int b_size = 0;
+
+       retv_if(!img, -1);
+       retv_if(size == 0, -1);
+
+       loader = exif_loader_new();
+       retv_if(!loader, -1);
+
+       ret = exif_loader_write(loader, img, size);
+       if (ret) {
+               _E("failed to loader write");
+               exif_loader_unref(loader);
+               return -1;
+       }
+
+       exif_loader_get_buf(loader, &buf, &b_size);
+       if (buf) {
+               _E("image has already exif data [%u]", b_size);
+               exif_loader_unref(loader);
+               return -1;
+       }
+
+       exif_loader_unref(loader);
+#endif /* CHECK_EXIF_BEFOR_CREATE */
+
+       return 0;
+}
+
+static ExifEntry *
+add_tag_by_init(ExifData *exif, ExifIfd ifd, ExifTag tag)
+{
+       ExifEntry *entry = NULL;
+
+       retv_if(!exif, NULL);
+
+       entry = exif_content_get_entry(exif->ifd[ifd], tag);
+       if (!entry) {
+               entry = exif_entry_new();
+               retv_if(!entry, NULL);
+
+               exif_entry_initialize(entry, tag);
+
+               entry->tag = tag;
+               exif_content_add_entry(exif->ifd[ifd], entry);
+
+               exif_entry_unref(entry);
+       }
+       return entry;
+}
+
+static ExifEntry *
+add_tag_by_malloc(ExifData *exif, ExifIfd ifd, ExifTag tag, unsigned int size)
+{
+       ExifEntry *entry = NULL;
+       void *buffer = NULL;
+       ExifMem *mem = NULL;
+
+       retv_if(!exif, NULL);
+       retv_if(size == 0, NULL);
+
+       mem = exif_mem_new_default();
+       retv_if(!mem, NULL);
+
+       entry = exif_entry_new_mem(mem);
+       if (!entry) {
+               _E("failed to exif_entry_new_mem()");
+               exif_mem_unref(mem);
+               return NULL;
+       }
+
+       buffer = exif_mem_alloc(mem, size);
+       if (!buffer) {
+               _E("failed to exif_mem_alloc()");
+               exif_mem_unref(mem);
+               exif_entry_unref(entry);
+               return NULL;
+       }
+
+       entry->data = buffer;
+       entry->size = size;
+       entry->tag = tag;
+       entry->components = size;
+       entry->format = EXIF_FORMAT_UNDEFINED;
+       exif_content_add_entry (exif->ifd[ifd], entry);
+
+       exif_mem_unref(mem);
+       exif_entry_unref(entry);
+
+       return entry;
+}
+
+static ExifEntry *
+add_tag_user_comment(ExifData *exif, const char *comment, unsigned int length)
+{
+       ExifEntry *entry = NULL;
+       size_t header_size = 0;
+
+       retv_if(!exif, NULL);
+       retv_if(!comment, NULL);
+       retv_if(length == 0, NULL);
+
+       header_size = sizeof(ASCII_COMMENT_HEADER) -1;
+
+       entry = add_tag_by_malloc(exif, EXIF_IFD_EXIF, EXIF_TAG_USER_COMMENT,
+                               header_size + length);
+
+       retv_if(!entry, NULL);
+
+       memcpy(entry->data, ASCII_COMMENT_HEADER, header_size);
+       memcpy(entry->data + header_size, comment, length);
+
+       return entry;
+}
+
+static int
+create_exif_data(const unsigned char *jpg_data, unsigned int jpg_size,
+               unsigned int jpg_width, unsigned int jpg_height,
+               const char *user_comment, unsigned int comment_len,
+               unsigned char **exif_data, unsigned int *exif_size)
+{
+
+       unsigned char *data;
+       unsigned int data_len;
+       ExifEntry *ee = NULL;
+       ExifData *exif = NULL;
+
+       retv_if(!jpg_data, -1);
+       retv_if(!exif_data, -1);
+       retv_if(!exif_size, -1);
+
+       if (check_exif_from_data(jpg_data, jpg_size))
+               return -1;
+
+       exif = exif_data_new();
+       retv_if(!exif, -1);
+
+       exif_data_set_option(exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
+       exif_data_set_data_type(exif, EXIF_DATA_TYPE_COMPRESSED);
+       exif_data_set_byte_order(exif, EXIF_BYTE_ORDER_INTEL);
+
+       exif_data_fix(exif);
+
+       ee = add_tag_by_init(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION);
+       exif_set_long(ee->data, EXIF_BYTE_ORDER_INTEL, jpg_width);
+
+       ee = add_tag_by_init(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
+       exif_set_long(ee->data, EXIF_BYTE_ORDER_INTEL, jpg_height);
+
+       ee = add_tag_user_comment(exif, user_comment, comment_len);
+
+       exif_data_save_data(exif, &data, &data_len);
+       if (!data) {
+               _E("failed to get exif data");
+               exif_data_unref(exif);
+               return -1;
+       }
+       exif_data_unref(exif);
+
+       *exif_data = data;
+       *exif_size = data_len;
+
+       return 0;
+}
+
+static int
+save_jpeg_file(const char *output_file,
+               const unsigned char *jpg_data, unsigned int jpg_size)
+{
+       FILE *fp = NULL;
+       retv_if(!output_file, -1);
+       retv_if(!jpg_data, -1);
+       retv_if(jpg_size <= 2, -1);
+
+       fp = fopen(output_file, "wb");
+       retv_if(!fp, -1);
+
+       if (1 != fwrite(jpg_data, jpg_size, 1, fp)) {
+               _E("failed to write jpg data");
+               fclose(fp);
+               return -1;
+       }
+
+       fflush(fp);
+       fclose(fp);
+
+       _D("file saved [%s]", output_file);
+
+       return 0;
+}
+
+static int
+save_jpeg_file_with_exif(const char *output_file,
+               const unsigned char *jpg_data, unsigned int jpg_size,
+               unsigned char *exif_data, unsigned int exif_size)
+{
+       FILE *fp = NULL;
+       const unsigned char exif_header[] = { 0xff, 0xd8, 0xff, 0xe1 }; // FFD8 -SOI Maker, FFE1 - APP1 Maker
+       const unsigned short exif_header_length = 4;
+       const unsigned short jpg_offset = 2; // SOI maker offset
+       unsigned short exif_length = 0;
+
+       ((unsigned char *)&exif_length)[0] = (exif_size + 2) >> 8;
+       ((unsigned char *)&exif_length)[1] = (exif_size + 2) & 0x00ff;
+
+       retv_if(!output_file, -1);
+       retv_if(!jpg_data, -1);
+       retv_if(!exif_data, -1);
+       retv_if(jpg_size <= 2, -1);
+       retv_if(exif_size == 0, -1);
+
+       fp = fopen(output_file, "wb");
+       retv_if(!fp, -1);
+
+       if (1 != fwrite(exif_header, exif_header_length, 1, fp)) {
+               _E("failed to write exif header");
+               goto ERROR;
+       }
+
+       if (1 != fwrite(&exif_length, sizeof(exif_length), 1, fp)) {
+               _E("failed to write exif legth");
+               goto ERROR;
+       }
+
+       if (1 != fwrite(exif_data, exif_size, 1, fp)) {
+               _E("failed to write exif");
+               goto ERROR;
+       }
+
+       if (1 != fwrite(jpg_data + jpg_offset, jpg_size - jpg_offset, 1, fp)) {
+               _E("failed to write jpg data");
+               goto ERROR;
+       }
+
+       fflush(fp);
+       fclose(fp);
+
+       return 0;
+ERROR:
+       fclose(fp);
+       unlink(output_file);
+       _E(" failed to save file - [%s]", output_file);
+       return -1;
+}
+
+int exif_write_jpg_file_with_comment(const char *output_file,
+               const unsigned char *jpg_data, unsigned int jpg_size,
+               unsigned int jpg_width, unsigned int jpg_height,
+               const char *comment, unsigned int comment_len)
+{
+       int ret = 0;
+       unsigned char *exif_data  = NULL;
+       unsigned int exif_size = 0;
+
+       if (!comment || (comment_len ==  0)) {
+               _W("There is no comment");
+               return save_jpeg_file(output_file, jpg_data, jpg_size);
+       }
+
+       ret = create_exif_data(jpg_data, jpg_size, jpg_width, jpg_height,
+                       comment, comment_len, &exif_data, &exif_size);
+       if (ret) {
+               _E("failed to create_exif_data(), save jpg data only");
+               return save_jpeg_file(output_file, jpg_data, jpg_size);
+       }
+
+       ret = save_jpeg_file_with_exif(
+                       output_file, jpg_data, jpg_size, exif_data, exif_size);
+
+       free(exif_data);
+       return ret;
+}
diff --git a/src/motion.c b/src/motion.c
new file mode 100644 (file)
index 0000000..33e028d
--- /dev/null
@@ -0,0 +1,184 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <glib.h>
+#include "log.h"
+#include "motion.h"
+
+#define IDLE_PRIORITY (G_PRIORITY_HIGH_IDLE + 30)
+
+struct motion_data_s {
+       int state;
+       GHashTable *callback_hash;
+};
+
+struct motion_callback_s {
+       motion_state_changed_cb callback;
+       void *cb_data;
+};
+
+struct motion_pass_data_s {
+       char *pass_key;
+       int state;
+};
+
+static struct motion_data_s *g_motion;
+static pthread_mutex_t g_motion_mutex;
+
+static int __free_motion_handle(struct motion_data_s *handle)
+{
+       if (handle->callback_hash) {
+               g_hash_table_destroy(handle->callback_hash);
+               g_hash_table_unref(handle->callback_hash);
+       }
+
+       free(handle);
+       return 0;
+}
+
+int motion_initialize(void)
+{
+       if (g_motion) {
+               _D("The motion is already initialized!");
+               return 0;
+       }
+
+       g_motion = malloc(sizeof(struct motion_data_s));
+       retv_if(!g_motion, -1);
+
+       g_motion->callback_hash =
+               g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+       pthread_mutex_init(&g_motion_mutex, NULL);
+       g_motion->state = 0;
+
+       /* TODO : initialize real motion detector here */
+
+       return 0;
+}
+
+int motion_finalize(void)
+{
+       if (g_motion) {
+               pthread_mutex_destroy(&g_motion_mutex);
+               __free_motion_handle(g_motion);
+               g_motion = NULL;
+       }
+
+       return 0;
+}
+
+static void __cb_item_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+       char *item_name = key;
+       struct motion_callback_s *cb_item = value;
+       struct motion_pass_data_s *pass_item = user_data;
+
+       ret_if(!item_name);
+       ret_if(!cb_item);
+       ret_if(!pass_item);
+
+
+       if (pass_item->pass_key &&
+               (0 == g_strcmp0(item_name, pass_item->pass_key))) {
+                       // _D("pass item - %s", item_name);
+                       return;
+               }
+
+       _D("callback called for [%s]", item_name);
+       cb_item->callback(pass_item->state, cb_item->cb_data);
+}
+
+static gboolean __call_cb_idle(gpointer data)
+{
+       struct motion_pass_data_s *pass_item = data;
+
+       g_hash_table_foreach(g_motion->callback_hash, __cb_item_foreach, pass_item);
+       g_free(pass_item->pass_key);
+       g_free(pass_item);
+
+       return G_SOURCE_REMOVE;
+}
+
+int motion_state_set(int state, const char *pass_key)
+{
+       int old_state = 0;
+       retv_if(!g_motion, -1);
+
+       // _D("set state : %d", state);
+
+       pthread_mutex_lock(&g_motion_mutex);
+       old_state = g_motion->state;
+       pthread_mutex_unlock(&g_motion_mutex);
+
+       if (old_state != state) {
+               /* TODO : handle real motion detector here */
+               pthread_mutex_lock(&g_motion_mutex);
+               g_motion->state = state;
+               pthread_mutex_unlock(&g_motion_mutex);
+
+               if (g_hash_table_size(g_motion->callback_hash) > 0) {
+                       struct motion_pass_data_s *pass_item = NULL;
+
+                       pass_item = g_new(struct motion_pass_data_s, 1);
+                       pass_item->pass_key = g_strdup(pass_key);
+                       pass_item->state = state;
+                       g_idle_add_full(IDLE_PRIORITY,
+                               __call_cb_idle, pass_item, NULL);
+               }
+       }
+       return 0;
+}
+
+int motion_state_get(int *state)
+{
+       retv_if(!g_motion, -1);
+       retv_if(!state, -1);
+
+       pthread_mutex_lock(&g_motion_mutex);
+       *state = g_motion->state;
+       pthread_mutex_unlock(&g_motion_mutex);
+
+       // _D("get state : %d", *state);
+
+       return 0;
+}
+
+int motion_state_changed_cb_set(
+       const char *callback_key, motion_state_changed_cb callback, void *cb_data)
+{
+       retv_if(!g_motion, -1);
+       retv_if(!g_motion->callback_hash, -1);
+       retv_if(!callback_key, -1);
+
+       if (callback) {
+               struct motion_callback_s *cb_item = NULL;
+               cb_item = g_try_new(struct motion_callback_s, 1);
+               retv_if(!cb_item, -1);
+
+               cb_item->callback = callback;
+               cb_item->cb_data = cb_data;
+
+               g_hash_table_insert(g_motion->callback_hash,
+                       g_strdup(callback_key), cb_item);
+       } else {
+               g_hash_table_remove(g_motion->callback_hash, callback_key);
+       }
+
+       return 0;
+}
diff --git a/src/resource_camera.c b/src/resource_camera.c
new file mode 100644 (file)
index 0000000..c76ccbb
--- /dev/null
@@ -0,0 +1,488 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <Ecore.h>
+#include <camera.h>
+#ifdef DEBUG_THREAD
+#include <pthread.h>
+#endif
+
+#include "log.h"
+#include "controller.h"
+#include "resource_camera.h"
+
+struct __camera_data {
+       camera_h cam_handle;
+
+       void *captured_file;
+       unsigned int image_size;
+
+       preview_image_buffer_created_cb preview_image_buffer_created_cb;
+       void *preview_image_buffer_created_cb_data;
+
+       capture_completed_cb capture_completed_cb;
+       void *capture_completed_cb_data;
+
+       bool is_af_enabled;
+};
+
+static struct __camera_data *g_camera_data = NULL;
+
+static const char * __cam_err_to_str(camera_error_e err)
+{
+       const char *err_str;
+
+       switch (err) {
+       case CAMERA_ERROR_NONE:
+               err_str = "CAMERA_ERROR_NONE";
+               break;
+       case CAMERA_ERROR_INVALID_PARAMETER:
+               err_str = "CAMERA_ERROR_INVALID_PARAMETER";
+               break;
+       case CAMERA_ERROR_INVALID_STATE:
+               err_str = "CAMERA_ERROR_INVALID_STATE";
+               break;
+       case CAMERA_ERROR_OUT_OF_MEMORY:
+               err_str = "CAMERA_ERROR_OUT_OF_MEMORY";
+               break;
+       case CAMERA_ERROR_DEVICE:
+               err_str = "CAMERA_ERROR_DEVICE";
+               break;
+       case CAMERA_ERROR_INVALID_OPERATION:
+               err_str = "CAMERA_ERROR_INVALID_OPERATION";
+               break;
+       case CAMERA_ERROR_SECURITY_RESTRICTED:
+               err_str = "CAMERA_ERROR_SECURITY_RESTRICTED";
+               break;
+       case CAMERA_ERROR_DEVICE_BUSY:
+               err_str = "CAMERA_ERROR_DEVICE_BUSY";
+               break;
+       case CAMERA_ERROR_DEVICE_NOT_FOUND:
+               err_str = "CAMERA_ERROR_DEVICE_NOT_FOUND";
+               break;
+       case CAMERA_ERROR_ESD:
+               err_str = "CAMERA_ERROR_ESD";
+               break;
+       case CAMERA_ERROR_PERMISSION_DENIED:
+               err_str = "CAMERA_ERROR_PERMISSION_DENIED";
+               break;
+       case CAMERA_ERROR_NOT_SUPPORTED:
+               err_str = "CAMERA_ERROR_NOT_SUPPORTED";
+               break;
+       case CAMERA_ERROR_RESOURCE_CONFLICT:
+               err_str = "CAMERA_ERROR_RESOURCE_CONFLICT";
+               break;
+       case CAMERA_ERROR_SERVICE_DISCONNECTED:
+               err_str = "CAMERA_ERROR_SERVICE_DISCONNECTED";
+               break;
+       default:
+               err_str = "Unknown";
+               break;
+       }
+       return err_str;
+}
+
+#ifdef DEBUG_THREAD
+static void __print_thread_id(char *msg)
+{
+       pthread_t id;
+       id = pthread_self();
+       _D("[%s] tid %u", msg,  (unsigned int)id);
+}
+#endif
+
+static void __print_camera_state(camera_state_e previous, camera_state_e current, bool by_policy, void *user_data)
+{
+       switch (current) {
+       case CAMERA_STATE_CREATED:
+               _D("Camera state: CAMERA_STATE_CREATED");
+               break;
+
+       case CAMERA_STATE_PREVIEW:
+               _D("Camera state: CAMERA_STATE_PREVIEW");
+               break;
+
+       case CAMERA_STATE_CAPTURING:
+               _D("Camera state: CAMERA_STATE_CAPTURING");
+               break;
+
+       case CAMERA_STATE_CAPTURED:
+               _D("Camera state: CAMERA_STATE_CAPTURED");
+               break;
+
+       default:
+               _D("Camera state: CAMERA_STATE_NONE");
+               break;
+       }
+}
+
+static long long int __get_monotonic_ms(void)
+{
+       long long int ret_time = 0;
+       struct timespec time_s;
+
+       if (0 == clock_gettime(CLOCK_MONOTONIC, &time_s))
+               ret_time = time_s.tv_sec* 1000 + time_s.tv_nsec / 1000000;
+       else
+               _E("Failed to get time");
+
+       return ret_time;
+}
+
+static image_buffer_data_s *__make_preview_image_buffer_data(camera_preview_data_s *frame)
+{
+       unsigned char *buffer = NULL;
+       unsigned int buffer_size = 0;
+
+       image_buffer_data_s *image_buffer = malloc(sizeof(image_buffer_data_s));
+       if (image_buffer == NULL) {
+               _E("Failed to allocate image buffer data");
+               goto ERROR;
+       }
+
+       switch (frame->num_of_planes) {
+       case 1:
+               buffer_size = frame->data.single_plane.size;
+               buffer = malloc(buffer_size);
+               if (!buffer) {
+                       _E("failed to malloc buffer");
+                       goto ERROR;
+               }
+               memcpy(buffer, frame->data.single_plane.yuv, buffer_size);
+               break;
+       case 2:
+               {
+                       unsigned char *buffer2 = NULL;
+                       buffer_size =
+                               frame->data.double_plane.y_size + frame->data.double_plane.uv_size;
+                       buffer = malloc(buffer_size);
+                       if (!buffer) {
+                               _E("failed to malloc buffer");
+                               goto ERROR;
+                       }
+                       buffer2 = buffer + frame->data.double_plane.y_size;
+                       memcpy(buffer,
+                               frame->data.double_plane.y, frame->data.double_plane.y_size);
+                       memcpy(buffer2,
+                               frame->data.double_plane.uv, frame->data.double_plane.uv_size);
+               }
+               break;
+       case 3:
+               {
+                       unsigned char *buffer2 = NULL;
+                       unsigned char *buffer3 = NULL;
+                       buffer_size = frame->data.triple_plane.y_size
+                                               + frame->data.triple_plane.u_size
+                                               + frame->data.triple_plane.v_size;
+                       buffer = malloc(buffer_size);
+                       if (!buffer) {
+                               _E("failed to malloc buffer");
+                               goto ERROR;
+                       }
+                       buffer2 = buffer + frame->data.triple_plane.y_size;
+                       buffer3 = buffer2 + frame->data.triple_plane.u_size;
+                       memcpy(buffer,
+                               frame->data.triple_plane.y, frame->data.triple_plane.y_size);
+                       memcpy(buffer2,
+                               frame->data.triple_plane.u, frame->data.triple_plane.u_size);
+                       memcpy(buffer3,
+                               frame->data.triple_plane.v, frame->data.triple_plane.v_size);
+               }
+               break;
+       default:
+               _E("unhandled num of planes : %d", frame->num_of_planes);
+               goto ERROR;
+               break;
+       }
+
+       image_buffer->image_width = frame->width;
+       image_buffer->image_height = frame->height;
+       image_buffer->buffer_size = buffer_size;
+       image_buffer->buffer = buffer;
+       image_buffer->format = frame->format;
+
+       return image_buffer;
+
+ERROR:
+       free(buffer);
+       free(image_buffer);
+
+       return NULL;
+}
+
+static bool __camera_attr_supported_af_mode_cb(camera_attr_af_mode_e mode, void *user_data)
+{
+       struct __camera_data *camera_data = user_data;
+
+       _I("Auto-Focus Mode [%d]", mode);
+
+       if (mode != CAMERA_ATTR_AF_NONE)
+               camera_data->is_af_enabled = true;
+
+       return true;
+}
+
+static void __capturing_cb(camera_image_data_s *image, camera_image_data_s *postview, camera_image_data_s *thumbnail, void *user_data)
+{
+       struct __camera_data *camera_data = user_data;
+       if (image == NULL) {
+               _E("Image is NULL");
+               return;
+       }
+
+       free(camera_data->captured_file);
+       camera_data->captured_file = malloc(image->size);
+       if (camera_data->captured_file == NULL)
+               return;
+
+       _D("Now is on Capturing: Image size[%d x %d]", image->width, image->height);
+
+       memcpy(camera_data->captured_file, image->data, image->size);
+       camera_data->image_size = image->size;
+
+       return;
+}
+
+static void __completed_cb(void *user_data)
+{
+       int ret = 0;
+       struct __camera_data *camera_data = user_data;
+
+       _D("Capture is completed");
+
+       if (camera_data->capture_completed_cb)
+               camera_data->capture_completed_cb(camera_data->captured_file, camera_data->image_size, camera_data->capture_completed_cb_data);
+
+       camera_data->capture_completed_cb = NULL;
+       free(camera_data->captured_file);
+       camera_data->captured_file = NULL;
+
+       if (!camera_data->cam_handle) {
+               _E("Camera is NULL");
+               return;
+       }
+
+       ret = camera_start_preview(camera_data->cam_handle);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to start preview [%s]", __cam_err_to_str(ret));
+               return;
+       }
+
+    return;
+}
+
+static void __start_capture(void *user_data)
+{
+       int ret = 0;
+       struct __camera_data *camera_data = user_data;
+
+       ret = camera_start_capture(camera_data->cam_handle, __capturing_cb, __completed_cb, camera_data);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to start capturing [%s]", __cam_err_to_str(ret));
+               return;
+       }
+}
+
+static void __camera_preview_cb(camera_preview_data_s *frame, void *user_data)
+{
+       struct __camera_data *camera_data = user_data;
+       static long long int last = 0;
+       long long int now = __get_monotonic_ms();
+
+       if (now - last < CAMERA_PREVIEW_INTERVAL_MIN)
+               return;
+
+       // _D("###Time Ellapse [%lld], [%lld], [%lld]", last, now, now - last);
+
+#ifdef DEBUG_THREAD
+       __print_thread_id("PREVIEW");
+#endif
+
+       image_buffer_data_s *image_buffer_data = __make_preview_image_buffer_data(frame);
+       if (image_buffer_data == NULL) {
+               _E("Failed to create mv source");
+               return;
+       }
+       image_buffer_data->user_data = camera_data->preview_image_buffer_created_cb_data;
+
+       ecore_main_loop_thread_safe_call_async(camera_data->preview_image_buffer_created_cb, image_buffer_data);
+       last = now;
+}
+
+int resource_camera_init(preview_image_buffer_created_cb preview_image_buffer_created_cb, void *user_data)
+{
+       int ret = CAMERA_ERROR_NONE;
+
+       if (preview_image_buffer_created_cb == NULL)
+               return -1;
+
+       g_camera_data = malloc(sizeof(struct __camera_data));
+       if (g_camera_data == NULL) {
+               _E("Failed to allocate Camera data");
+               return -1;
+       }
+       memset(g_camera_data, 0, sizeof(struct __camera_data));
+
+       ret = camera_create(CAMERA_DEVICE_CAMERA0, &(g_camera_data->cam_handle));
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to create camera [%s]", __cam_err_to_str(ret));
+               goto ERROR;
+       }
+
+       ret = camera_attr_set_image_quality(g_camera_data->cam_handle, CAMERA_IMAGE_QUALITY);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to set image quality [%s]", __cam_err_to_str(ret));
+               goto ERROR;
+       }
+
+       ret = camera_set_preview_resolution(g_camera_data->cam_handle, IMAGE_WIDTH, IMAGE_HEIGHT);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to set preview resolution [%s]", __cam_err_to_str(ret));
+               goto ERROR;
+       }
+
+       ret = camera_set_capture_resolution(g_camera_data->cam_handle, IMAGE_WIDTH, IMAGE_HEIGHT);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to set capture resolution [%s]", __cam_err_to_str(ret));
+               goto ERROR;
+       }
+
+       ret = camera_set_capture_format(g_camera_data->cam_handle, CAMERA_PIXEL_FORMAT_JPEG);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to set capture format [%s]", __cam_err_to_str(ret));
+               goto ERROR;
+       }
+
+       ret = camera_set_state_changed_cb(g_camera_data->cam_handle, __print_camera_state, NULL);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to set state changed callback [%s]", __cam_err_to_str(ret));
+               goto ERROR;
+       }
+
+       ret = camera_set_preview_cb(g_camera_data->cam_handle, __camera_preview_cb, g_camera_data);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to set preview callback [%s]", __cam_err_to_str(ret));
+               goto ERROR;
+       }
+
+       ret = camera_attr_foreach_supported_af_mode(g_camera_data->cam_handle, __camera_attr_supported_af_mode_cb, g_camera_data);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to set auto focus attribute check callback [%s]", __cam_err_to_str(ret));
+               goto ERROR;
+       }
+
+       g_camera_data->preview_image_buffer_created_cb = preview_image_buffer_created_cb;
+       g_camera_data->preview_image_buffer_created_cb_data = user_data;
+
+       return 0;
+
+ERROR:
+       if (g_camera_data->cam_handle)
+               camera_destroy(g_camera_data->cam_handle);
+
+       free(g_camera_data);
+       g_camera_data = NULL;
+       return -1;
+}
+
+int resource_camera_start_preview(void)
+{
+       camera_state_e state;
+       int ret = CAMERA_ERROR_NONE;
+
+       if (g_camera_data == NULL) {
+               _I("Camera is not initialized");
+               return -1;
+       }
+
+       ret = camera_get_state(g_camera_data->cam_handle, &state);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to get camera state [%s]", __cam_err_to_str(ret));
+               return -1;
+       }
+
+       if (state == CAMERA_STATE_CAPTURING) {
+               _D("Camera is now capturing");
+               return -1;
+       }
+
+       if (state != CAMERA_STATE_PREVIEW) {
+               ret = camera_start_preview(g_camera_data->cam_handle);
+               if (ret != CAMERA_ERROR_NONE) {
+                       _E("Failed to start preview [%s]", __cam_err_to_str(ret));
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+int resource_camera_capture(capture_completed_cb capture_completed_cb, void *user_data)
+{
+       camera_state_e state;
+       int ret = CAMERA_ERROR_NONE;
+
+       if (g_camera_data == NULL) {
+               _I("Camera is not initialized");
+               return -1;
+       }
+
+       ret = camera_get_state(g_camera_data->cam_handle, &state);
+       if (ret != CAMERA_ERROR_NONE) {
+               _E("Failed to get camera state [%s]", __cam_err_to_str(ret));
+               return -1;
+       }
+
+       if (state == CAMERA_STATE_CAPTURING) {
+               _D("Camera is now capturing");
+               return -1;
+       }
+
+       if (state != CAMERA_STATE_PREVIEW) {
+               _I("Preview is not started [%d]", state);
+               ret = camera_start_preview(g_camera_data->cam_handle);
+               if (ret != CAMERA_ERROR_NONE) {
+                       _E("Failed to start preview [%s]", __cam_err_to_str(ret));
+                       return -1;
+               }
+       }
+
+       __start_capture(g_camera_data);
+
+       g_camera_data->capture_completed_cb = capture_completed_cb;
+       g_camera_data->capture_completed_cb_data = user_data;
+
+       return 0;
+}
+
+void resource_camera_close(void)
+{
+       if (g_camera_data == NULL)
+               return;
+
+       camera_unset_preview_cb(g_camera_data->cam_handle);
+       camera_stop_preview(g_camera_data->cam_handle);
+
+       camera_destroy(g_camera_data->cam_handle);
+       g_camera_data->cam_handle = NULL;
+
+       free(g_camera_data->captured_file);
+       g_camera_data->captured_file = NULL;
+
+       free(g_camera_data);
+       g_camera_data = NULL;
+}
diff --git a/src/resource_servo_motor_sg90.c b/src/resource_servo_motor_sg90.c
new file mode 100644 (file)
index 0000000..d30cce4
--- /dev/null
@@ -0,0 +1,166 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <peripheral_io.h>
+#include <glib.h>
+#include "log.h"
+
+#define ENABLE_SERVO_TIMEOUT
+
+/* This value is only for Servo Motor - SG90 */
+#define SERVO_MOTOR_DEFAULT_PERIOD 20.0
+#define SERVO_MOTOR_DUTY_CYCLE_COUNTER_CLOCKWISE 0.9
+#define SERVO_MOTOR_DUTY_CYCLE_CLOCKWISE 2.4
+
+#ifdef ENABLE_SERVO_TIMEOUT
+#define SERVO_TIMEOUT_INTERVAL 150
+#endif /* ENABLE_SERVO_TIMEOUT */
+
+/* This APIs are only made for Eagleye530s */
+/* There are 2 pins for PWM */
+static peripheral_pwm_h g_pwm0_h;
+static peripheral_pwm_h g_pwm2_h;
+
+#ifdef ENABLE_SERVO_TIMEOUT
+static guint g_timer_id0;
+static guint g_timer_id2;
+
+static gboolean __timeout_cb(void *data)
+{
+       int channel = GPOINTER_TO_INT(data);
+
+       _D("pwm channel[%d] disable", channel);
+       if (channel == 0 && g_pwm0_h) {
+               peripheral_pwm_set_enabled(g_pwm0_h, false);
+               g_timer_id0 = 0;
+       } else if (channel == 2 && g_pwm2_h) {
+               peripheral_pwm_set_enabled(g_pwm2_h, false);
+               g_timer_id2 = 0;
+       }
+       return FALSE;
+}
+
+static void __remove_timeout_cb(int channel)
+{
+       if (channel == 0) {
+               if (g_timer_id0) {
+                       g_source_remove(g_timer_id0);
+                       g_timer_id0 = 0;
+               }
+       } else if (channel == 2) {
+               if (g_timer_id2) {
+                       g_source_remove(g_timer_id0);
+                       g_timer_id2 = 0;
+               }
+       }
+}
+
+static void __add_timeout_cb(int channel)
+{
+       if (channel == 0) {
+               g_timer_id0 = g_timeout_add(SERVO_TIMEOUT_INTERVAL,
+                                               __timeout_cb, GINT_TO_POINTER(channel));
+       } else if (channel == 2) {
+               g_timer_id2 = g_timeout_add(SERVO_TIMEOUT_INTERVAL,
+                                               __timeout_cb, GINT_TO_POINTER(channel));
+       }
+}
+#endif /* ENABLE_SERVO_TIMEOUT */
+
+void resource_close_servo_motor(int channel)
+{
+#ifdef ENABLE_SERVO_TIMEOUT
+       __remove_timeout_cb(channel);
+#endif /* ENABLE_SERVO_TIMEOUT */
+
+       if (channel == 0 && g_pwm0_h) {
+               peripheral_pwm_close(g_pwm0_h);
+               g_pwm0_h = NULL;
+       } else if (channel == 2 && g_pwm2_h) {
+               peripheral_pwm_close(g_pwm2_h);
+               g_pwm2_h = NULL;
+       }
+}
+
+int resource_set_servo_motor_sg90_value(int channel, double duty_cycle_ms)
+{
+       int ret = 0;
+       peripheral_pwm_h *pwm_h = NULL;
+
+       if (duty_cycle_ms >= SERVO_MOTOR_DEFAULT_PERIOD) {
+               _E("Too large duty cycle");
+               return -1;
+       }
+
+#ifdef ENABLE_SERVO_TIMEOUT
+       __remove_timeout_cb(channel);
+#endif /* ENABLE_SERVO_TIMEOUT */
+
+       if (channel == 0) {
+               pwm_h = &g_pwm0_h;
+       } else if (channel == 2) {
+               pwm_h = &g_pwm2_h;
+       } else {
+               _E("unsupported channel - %d", channel);
+               return -1;
+       }
+
+       if (!*pwm_h) {
+               ret = peripheral_pwm_open(0, channel, pwm_h);
+               if (ret != PERIPHERAL_ERROR_NONE) {
+                       _E("failed to open servo motor with channel(%d) : %s", channel, get_error_message(ret));
+                       return -1;
+               }
+       }
+
+       ret = peripheral_pwm_set_period(*pwm_h, SERVO_MOTOR_DEFAULT_PERIOD * 1000 * 1000);
+       if (ret != PERIPHERAL_ERROR_NONE) {
+               _E("failed to set period : %s", get_error_message(ret));
+               return -1;
+       }
+
+       ret = peripheral_pwm_set_duty_cycle(*pwm_h, duty_cycle_ms * 1000 * 1000);
+       if (ret != PERIPHERAL_ERROR_NONE) {
+               _E("failed to set duty cycle : %s", get_error_message(ret));
+               return -1;
+       }
+
+       ret = peripheral_pwm_set_enabled(*pwm_h, true);
+       if (ret != PERIPHERAL_ERROR_NONE) {
+               _E("failed to enable : %s", get_error_message(ret));
+               return -1;
+       }
+
+#ifdef ENABLE_SERVO_TIMEOUT
+       __add_timeout_cb(channel);
+#endif /* ENABLE_SERVO_TIMEOUT */
+
+       return 0;
+}
+
+int resource_rotate_servo_motor_by_percent(int channel, double percent)
+{
+       int ret = 0;
+       double duty_cycle = 0.0f;
+
+       duty_cycle = ((SERVO_MOTOR_DUTY_CYCLE_CLOCKWISE - SERVO_MOTOR_DUTY_CYCLE_COUNTER_CLOCKWISE)
+                       * (percent / 100.0))
+                       + SERVO_MOTOR_DUTY_CYCLE_COUNTER_CLOCKWISE;
+
+       ret = resource_set_servo_motor_sg90_value(channel, duty_cycle);
+
+       return ret;
+}
diff --git a/src/servo-h.c b/src/servo-h.c
new file mode 100644 (file)
index 0000000..77c9434
--- /dev/null
@@ -0,0 +1,205 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <glib.h>
+#include <math.h>
+#include "log.h"
+#include "servo-type.h"
+#include "resource_servo_motor_sg90.h"
+
+#define IDLE_PRIORITY (G_PRIORITY_HIGH_IDLE + 30)
+#define VALUE_DEFAULT (50.0f)
+#define SERVO_H_CHANNEL 2
+
+struct servo_h_data_s {
+       double value;
+       GHashTable *callback_hash;
+};
+
+struct servo_h_callback_s {
+       servo_state_changed_cb callback;
+       void *cb_data;
+};
+
+struct servo_h_pass_data_s {
+       char *pass_key;
+       double value;
+};
+
+static struct servo_h_data_s *g_servo_h;
+static pthread_mutex_t g_servo_h_mutex;
+
+static int __free_servo_h_handle(struct servo_h_data_s *servo_h)
+{
+       if (servo_h->callback_hash) {
+               g_hash_table_destroy(servo_h->callback_hash);
+               g_hash_table_unref(servo_h->callback_hash);
+       }
+
+       free(servo_h);
+       return 0;
+}
+
+int servo_h_finalize(void)
+{
+       if (g_servo_h) {
+               resource_close_servo_motor(SERVO_H_CHANNEL);
+               pthread_mutex_destroy(&g_servo_h_mutex);
+               __free_servo_h_handle(g_servo_h);
+               g_servo_h = NULL;
+       }
+       return 0;
+}
+
+int servo_h_initialize(void)
+{
+       if (g_servo_h) {
+               _D("The servo_h is already initialized!");
+               return 0;
+       }
+
+       g_servo_h = malloc(sizeof(struct servo_h_data_s));
+       retv_if(!g_servo_h, -1);
+
+       g_servo_h->callback_hash =
+               g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+       pthread_mutex_init(&g_servo_h_mutex, NULL);
+       g_servo_h->value = VALUE_DEFAULT;
+
+       if (resource_rotate_servo_motor_by_percent(SERVO_H_CHANNEL, VALUE_DEFAULT)) {
+               _E("faiiled to set servo h value to 0");
+               servo_h_finalize();
+               return -1;
+       }
+
+       return 0;
+}
+
+static void __cb_item_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+       char *item_name = key;
+       struct servo_h_callback_s *cb_item = value;
+       struct servo_h_pass_data_s *pass_item = user_data;
+
+       ret_if(!item_name);
+       ret_if(!cb_item);
+       ret_if(!pass_item);
+
+
+       if (pass_item->pass_key &&
+               (0 == g_strcmp0(item_name, pass_item->pass_key))) {
+                       // _D("pass item - %s", item_name);
+                       return;
+               }
+
+       _D("callback called for [%s]", item_name);
+       cb_item->callback(pass_item->value, cb_item->cb_data);
+}
+
+static gboolean __call_cb_idle(gpointer data)
+{
+       struct servo_h_pass_data_s *pass_item = data;
+
+       g_hash_table_foreach(g_servo_h->callback_hash, __cb_item_foreach, pass_item);
+       g_free(pass_item->pass_key);
+       g_free(pass_item);
+
+       return G_SOURCE_REMOVE;
+}
+
+static inline int __double_is_same(double a, double b)
+{
+       const double eps = 0.0001;
+       if (fabs(a-b) < eps)
+               return 1;
+       else
+               return 0;
+}
+
+int servo_h_state_set(double value, const char *pass_key)
+{
+       double old_value = 0.0;
+       retv_if(!g_servo_h, -1);
+
+       pthread_mutex_lock(&g_servo_h_mutex);
+       old_value = g_servo_h->value;
+       pthread_mutex_unlock(&g_servo_h_mutex);
+
+       if (!__double_is_same(old_value, value)) {
+               if (resource_rotate_servo_motor_by_percent(SERVO_H_CHANNEL, value)) {
+                       _E("faiiled to set servo h value to [%lf]", value);
+                       return -1;
+               }
+               _D("set value : %lf", value);
+               pthread_mutex_lock(&g_servo_h_mutex);
+               g_servo_h->value = value;
+               pthread_mutex_unlock(&g_servo_h_mutex);
+
+               if (g_hash_table_size(g_servo_h->callback_hash) > 0) {
+                       struct servo_h_pass_data_s *pass_item = NULL;
+
+                       pass_item = g_new(struct servo_h_pass_data_s, 1);
+                       pass_item->pass_key = g_strdup(pass_key);
+                       pass_item->value = value;
+                       g_idle_add_full(IDLE_PRIORITY,
+                               __call_cb_idle, pass_item, NULL);
+               }
+       } else {
+               _D("a value[%lf] is same as old one[%lf]" , value, old_value);
+       }
+       return 0;
+}
+
+int servo_h_state_get(double *value)
+{
+       retv_if(!g_servo_h, -1);
+       retv_if(!value, -1);
+
+       pthread_mutex_lock(&g_servo_h_mutex);
+       *value = g_servo_h->value;
+       pthread_mutex_unlock(&g_servo_h_mutex);
+
+       _D("get value : %lf", *value);
+
+       return 0;
+}
+
+int servo_h_state_changed_cb_set(
+       const char *callback_key, servo_state_changed_cb callback, void *cb_data)
+{
+       retv_if(!g_servo_h, -1);
+       retv_if(!g_servo_h->callback_hash, -1);
+       retv_if(!callback_key, -1);
+
+       if (callback) {
+               struct servo_h_callback_s *cb_item = NULL;
+               cb_item = g_try_new(struct servo_h_callback_s, 1);
+               retv_if(!cb_item, -1);
+
+               cb_item->callback = callback;
+               cb_item->cb_data = cb_data;
+
+               g_hash_table_insert(g_servo_h->callback_hash,
+                       g_strdup(callback_key), cb_item);
+       } else {
+               g_hash_table_remove(g_servo_h->callback_hash, callback_key);
+       }
+
+       return 0;
+}
diff --git a/src/servo-v.c b/src/servo-v.c
new file mode 100644 (file)
index 0000000..e1cf675
--- /dev/null
@@ -0,0 +1,205 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <glib.h>
+#include <math.h>
+#include "log.h"
+#include "servo-type.h"
+#include "resource_servo_motor_sg90.h"
+
+#define IDLE_PRIORITY (G_PRIORITY_HIGH_IDLE + 30)
+#define VALUE_DEFAULT (50.0f)
+#define SERVO_V_CHANNEL 0
+
+struct servo_v_data_s {
+       double value;
+       GHashTable *callback_hash;
+};
+
+struct servo_v_callback_s {
+       servo_state_changed_cb callback;
+       void *cb_data;
+};
+
+struct servo_v_pass_data_s {
+       char *pass_key;
+       double value;
+};
+
+static struct servo_v_data_s *g_servo_v;
+static pthread_mutex_t g_servo_v_mutex;
+
+static int __free_servo_v_handle(struct servo_v_data_s *servo_v)
+{
+       if (servo_v->callback_hash) {
+               g_hash_table_destroy(servo_v->callback_hash);
+               g_hash_table_unref(servo_v->callback_hash);
+       }
+
+       free(servo_v);
+       return 0;
+}
+
+int servo_v_finalize(void)
+{
+       if (g_servo_v) {
+               resource_close_servo_motor(SERVO_V_CHANNEL);
+               pthread_mutex_destroy(&g_servo_v_mutex);
+               __free_servo_v_handle(g_servo_v);
+               g_servo_v = NULL;
+       }
+
+       return 0;
+}
+
+int servo_v_initialize(void)
+{
+       if (g_servo_v) {
+               _D("The servo_v is already initialized!");
+               return 0;
+       }
+
+       g_servo_v = malloc(sizeof(struct servo_v_data_s));
+       retv_if(!g_servo_v, -1);
+
+       g_servo_v->callback_hash =
+               g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+       pthread_mutex_init(&g_servo_v_mutex, NULL);
+       g_servo_v->value = VALUE_DEFAULT;
+
+       if (resource_rotate_servo_motor_by_percent(SERVO_V_CHANNEL, VALUE_DEFAULT)) {
+               _E("faiiled to set servo h value to 0");
+               servo_v_finalize();
+               return -1;
+       }
+
+       return 0;
+}
+
+static void __cb_item_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+       char *item_name = key;
+       struct servo_v_callback_s *cb_item = value;
+       struct servo_v_pass_data_s *pass_item = user_data;
+
+       ret_if(!item_name);
+       ret_if(!cb_item);
+       ret_if(!pass_item);
+
+       if (pass_item->pass_key &&
+               (0 == g_strcmp0(item_name, pass_item->pass_key))) {
+                       // _D("pass item - %s", item_name);
+                       return;
+               }
+
+       _D("callback called for [%s]", item_name);
+       cb_item->callback(pass_item->value, cb_item->cb_data);
+}
+
+static gboolean __call_cb_idle(gpointer data)
+{
+       struct servo_v_pass_data_s *pass_item = data;
+
+       g_hash_table_foreach(g_servo_v->callback_hash, __cb_item_foreach, pass_item);
+       g_free(pass_item->pass_key);
+       g_free(pass_item);
+
+       return G_SOURCE_REMOVE;
+}
+
+static inline int __double_is_same(double a, double b)
+{
+       const double eps = 0.0001;
+       if (fabs(a-b) < eps)
+               return 1;
+       else
+               return 0;
+}
+
+int servo_v_state_set(double value, const char *pass_key)
+{
+       double old_value = 0.0;
+       retv_if(!g_servo_v, -1);
+
+       pthread_mutex_lock(&g_servo_v_mutex);
+       old_value = g_servo_v->value;
+       pthread_mutex_unlock(&g_servo_v_mutex);
+
+       if (!__double_is_same(old_value, value)) {
+               if (resource_rotate_servo_motor_by_percent(SERVO_V_CHANNEL, value)) {
+                       _E("faiiled to set servo v value to [%lf]", value);
+                       return -1;
+               }
+               _D("set value : %lf", value);
+               pthread_mutex_lock(&g_servo_v_mutex);
+               g_servo_v->value = value;
+               pthread_mutex_unlock(&g_servo_v_mutex);
+
+               if (g_hash_table_size(g_servo_v->callback_hash) > 0) {
+                       struct servo_v_pass_data_s *pass_item = NULL;
+
+                       pass_item = g_new(struct servo_v_pass_data_s, 1);
+                       pass_item->pass_key = g_strdup(pass_key);
+                       pass_item->value = value;
+                       g_idle_add_full(IDLE_PRIORITY,
+                               __call_cb_idle, pass_item, NULL);
+               }
+       } else {
+               _D("a value[%lf] is same as old one[%lf]" , value, old_value);
+       }
+       return 0;
+}
+
+int servo_v_state_get(double *value)
+{
+       retv_if(!g_servo_v, -1);
+       retv_if(!value, -1);
+
+       pthread_mutex_lock(&g_servo_v_mutex);
+       *value = g_servo_v->value;
+       pthread_mutex_unlock(&g_servo_v_mutex);
+
+       _D("get value : %lf", *value);
+
+       return 0;
+}
+
+int servo_v_state_changed_cb_set(
+       const char *callback_key, servo_state_changed_cb callback, void *cb_data)
+{
+       retv_if(!g_servo_v, -1);
+       retv_if(!g_servo_v->callback_hash, -1);
+       retv_if(!callback_key, -1);
+
+       if (callback) {
+               struct servo_v_callback_s *cb_item = NULL;
+               cb_item = g_try_new(struct servo_v_callback_s, 1);
+               retv_if(!cb_item, -1);
+
+               cb_item->callback = callback;
+               cb_item->cb_data = cb_data;
+
+               g_hash_table_insert(g_servo_v->callback_hash,
+                       g_strdup(callback_key), cb_item);
+       } else {
+               g_hash_table_remove(g_servo_v->callback_hash, callback_key);
+       }
+
+       return 0;
+}
diff --git a/src/st_thing_master.c b/src/st_thing_master.c
new file mode 100644 (file)
index 0000000..edb1fe4
--- /dev/null
@@ -0,0 +1,271 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <smartthings.h>
+#include "log.h"
+
+/*  You have to FIX IT !!!  */
+#define CERT_FILE "cert_file_name.pem" // cert file name in 'res' directory
+#define PRIV_FILE "private_key_file_name.der" // private key file name in 'res' directory
+
+static smartthings_h g_st_master_h;
+
+static void _user_confirm_cb(smartthings_h handle, void *user_data)
+{
+       if (smartthings_send_user_confirm(handle, true) != 0)
+               _E("smartthings_send_user_confirm() is failed");
+}
+
+static void _reset_confirm_cb(smartthings_h handle, void *user_data)
+{
+       if (smartthings_send_reset_confirm(handle, true) != 0)
+               _E("smartthings_send_reset_confirm() is failed");
+}
+
+static void _reset_result_cb(smartthings_h handle, bool result, void *user_data)
+{
+       _I("reset result = [%d]", result);
+}
+
+static const char *thing_status_to_str(smartthings_status_e status)
+{
+       const char *status_str = NULL;
+
+       switch (status) {
+       case SMARTTHINGS_STATUS_NOT_READY:
+               status_str = "SMARTTHINGS_STATUS_NOT_READY";
+               break;
+       case SMARTTHINGS_STATUS_INIT:
+               status_str = "SMARTTHINGS_STATUS_INIT";
+               break;
+       case SMARTTHINGS_STATUS_ES_STARTED:
+               status_str = "SMARTTHINGS_STATUS_ES_STARTED";
+               break;
+       case SMARTTHINGS_STATUS_ES_DONE:
+               status_str =  "SMARTTHINGS_STATUS_ES_DONE";
+               break;
+       case SMARTTHINGS_STATUS_ES_FAILED_ON_OWNERSHIP_TRANSFER:
+               status_str = "SMARTTHINGS_STATUS_ES_FAILED_ON_OWNERSHIP_TRANSFER";
+               break;
+       case SMARTTHINGS_STATUS_CONNECTING_TO_AP:
+               status_str = "SMARTTHINGS_STATUS_CONNECTING_TO_AP";
+               break;
+       case SMARTTHINGS_STATUS_CONNECTED_TO_AP:
+               status_str = "SMARTTHINGS_STATUS_CONNECTED_TO_AP";
+               break;
+       case SMARTTHINGS_STATUS_CONNECTING_TO_AP_FAILED:
+               status_str = "SMARTTHINGS_STATUS_CONNECTING_TO_AP_FAILED";
+               break;
+       case SMARTTHINGS_STATUS_REGISTERING_TO_CLOUD:
+               status_str = "SMARTTHINGS_STATUS_REGISTERING_TO_CLOUD";
+               break;
+       case SMARTTHINGS_STATUS_REGISTERED_TO_CLOUD:
+               status_str = "SMARTTHINGS_STATUS_REGISTERED_TO_CLOUD";
+               break;
+       case SMARTTHINGS_STATUS_REGISTERING_FAILED_ON_SIGN_IN:
+               status_str = "SMARTTHINGS_STATUS_REGISTERING_FAILED_ON_SIGN_IN";
+               break;
+       case SMARTTHINGS_STATUS_REGISTERING_FAILED_ON_PUB_RES:
+               status_str = "SMARTTHINGS_STATUS_REGISTERING_FAILED_ON_PUB_RES";
+               break;
+       default:
+               status_str = "Unknown Status";
+               break;
+       }
+       return status_str;
+}
+
+static void
+_thing_status_cb(
+       smartthings_h handle, smartthings_status_e status, void *user_data)
+{
+       _D("status: [%d] [%s]", status, thing_status_to_str(status));
+}
+
+static const char *__master_error_to_str(smartthings_error_e error)
+{
+       const char *err_str = NULL;
+
+       switch (error) {
+       case SMARTTHINGS_ERROR_NONE:
+               err_str = "SMARTTHINGS_ERROR_NONE";
+               break;
+       case SMARTTHINGS_ERROR_INVALID_PARAMETER:
+               err_str = "SMARTTHINGS_ERROR_INVALID_PARAMETER";
+               break;
+       case SMARTTHINGS_ERROR_OUT_OF_MEMORY:
+               err_str = "SMARTTHINGS_ERROR_OUT_OF_MEMORY";
+               break;
+       case SMARTTHINGS_ERROR_PERMISSION_DENIED:
+               err_str = "SMARTTHINGS_ERROR_PERMISSION_DENIED";
+               break;
+       case SMARTTHINGS_ERROR_NO_DATA:
+               err_str = "SMARTTHINGS_ERROR_NO_DATA";
+               break;
+       case SMARTTHINGS_ERROR_NOT_SUPPORTED:
+               err_str = "SMARTTHINGS_ERROR_NOT_SUPPORTED";
+               break;
+       case SMARTTHINGS_ERROR_OPERATION_FAILED:
+               err_str = "SMARTTHINGS_ERROR_OPERATION_FAILED";
+               break;
+       case SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE:
+               err_str = "SMARTTHINGS_ERROR_SERVICE_UNAVAILABLE";
+               break;
+       default:
+               err_str = "Unknown error";
+               break;
+       }
+
+       return err_str;
+}
+
+static void
+_things_connection_status_cb(smartthings_error_e error,
+       smartthings_h handle, smartthings_connection_status_e status,
+       void *user_data)
+{
+       _D("result [%s], status = [%d]", __master_error_to_str(error), status);
+
+       if (status == SMARTTHINGS_CONNECTION_STATUS_CONNECTED) {
+               int err = 0;
+               bool is_es_completed = false;
+               const char* dev_name = "iot-vision-cam";
+               int wifi_mode = SMARTTHINGS_WIFI_MODE_11B
+                       | SMARTTHINGS_WIFI_MODE_11G
+                       | SMARTTHINGS_WIFI_MODE_11N;
+
+               int wifi_freq = SMARTTHINGS_WIFI_FREQ_24G | SMARTTHINGS_WIFI_FREQ_5G;
+
+               err = smartthings_set_device_property(
+                                       handle, dev_name, wifi_mode, wifi_freq);
+               if (err) {
+                       _E("smartthings_initialize() is failed, [%s]",
+                               __master_error_to_str(err));
+                       return;
+               }
+
+               err = smartthings_set_certificate_file(handle, CERT_FILE, PRIV_FILE);
+               if (err) {
+                       _E("smartthings_set_certificate_file() is failed, [%s]",
+                               __master_error_to_str(err));
+                       return;
+               }
+
+               err = smartthings_set_user_confirm_cb(handle, _user_confirm_cb, NULL);
+               if (err) {
+                       _E("smartthings_set_user_confirm_cb() is failed, [%s]",
+                               __master_error_to_str(err));
+                       return;
+               }
+
+               err = smartthings_set_reset_confirm_cb(handle, _reset_confirm_cb, NULL);
+               if (err) {
+                       _E("smartthings_set_reset_confirm_cb() is failed, [%s]",
+                               __master_error_to_str(err));
+                       return;
+               }
+
+               err = smartthings_set_reset_result_cb(handle, _reset_result_cb, NULL);
+               if (err) {
+                       _E("smartthings_set_reset_confirm_cb() is failed, [%s]",
+                               __master_error_to_str(err));
+                       return;
+               }
+
+               err = smartthings_set_status_changed_cb(handle, _thing_status_cb, NULL);
+               if (err) {
+                       _E("smartthings_set_status_changed_callback() is failed, [%s]",
+                               __master_error_to_str(err));
+                       return;
+               }
+
+               err = smartthings_start(handle);
+               if (err) {
+                       _E("smartthings_start() is failed, [%s]",
+                               __master_error_to_str(err));
+                       return;
+               }
+
+               err = smartthings_get_easysetup_status(handle, &is_es_completed);
+               if (err) {
+                       _E("smartthings_get_easysetup_status() is failed, [%s]",
+                               __master_error_to_str(err));
+                       return;
+               }
+
+               if (is_es_completed == true) {
+                       _I("Easysetup is already done");
+                       return;
+               }
+
+               err = smartthings_start_easysetup(handle);
+               if (err) {
+                       _E("smartthings_start_easysetup() is failed, [%s]",
+                               __master_error_to_str(err));
+                       smartthings_stop(handle);
+                       return;
+               }
+       } else {
+                       _E("connection failed");
+       }
+       return;
+}
+
+int st_thing_master_init()
+{
+       int err = 0;
+       smartthings_h st_handle = NULL;
+
+       if (g_st_master_h) {
+               _I("Already initialized!");
+               return 0;
+       }
+
+       err = smartthings_initialize(&st_handle, _things_connection_status_cb, NULL);
+       if (err) {
+               _E("smartthings_initialize() is failed, [%s]",
+                       __master_error_to_str(err));
+               return -1;
+       }
+
+       g_st_master_h = st_handle;
+
+       return 0;
+}
+
+int st_thing_master_fini()
+{
+       if (!g_st_master_h) {
+               _I("handle is already NULL");
+               return 0;
+       }
+
+       smartthings_unset_user_confirm_cb(g_st_master_h);
+       smartthings_unset_reset_confirm_cb(g_st_master_h);
+       smartthings_unset_reset_result_cb(g_st_master_h);
+       smartthings_unset_status_changed_cb(g_st_master_h);
+
+       smartthings_stop_easysetup(g_st_master_h);
+       smartthings_stop(g_st_master_h);
+
+       if (smartthings_deinitialize(g_st_master_h) != 0)  {
+               _E("smartthings_deinitialize() is failed");
+               return -1;
+       }
+       g_st_master_h = NULL;
+
+       return 0;
+}
diff --git a/src/st_thing_resource.c b/src/st_thing_resource.c
new file mode 100644 (file)
index 0000000..6dcb842
--- /dev/null
@@ -0,0 +1,478 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <smartthings_resource.h>
+#include <smartthings_payload.h>
+#include <glib.h>
+#include "log.h"
+#include "switch.h"
+#include "servo-h.h"
+#include "servo-v.h"
+#include "motion.h"
+
+// switch
+#define URI_SWITCH "/capability/switch/main/0"
+#define KEY_SWITCH "power"
+#define VALUE_SWITCH_ON "on"
+#define VALUE_SWITCH_OFF "off"
+
+// volume - servo v
+#define URI_AUDIOVOLUME "/capability/audioVolume/main/0"
+#define KEY_VOLUME "volume"
+#define KEY_MUTE "mute"
+
+// dim switch - servo h
+#define URI_SWITCHLEVEL "/capability/switchLevel/main/0"
+#define KEY_SWITCHLEVEL "dimmingSetting"
+
+// motion
+#define URI_MOTION "/capability/motionSensor/main/0"
+#define KEY_MOTION "value"
+
+#define RESOURCE_CALLBACK_KEY "st_thing_resource"
+
+static smartthings_resource_h g_st_res_h;
+static smartthings_resource_connection_status_e g_conn_status =
+               SMARTTHINGS_RESOURCE_CONNECTION_STATUS_DISCONNECTED;
+
+
+static const char *
+__resource_error_to_str(smartthings_resource_error_e error)
+{
+       const char *err_str = NULL;
+
+       switch (error) {
+       case SMARTTHINGS_RESOURCE_ERROR_NONE:
+               err_str = "SMARTTHINGS_RESOURCE_ERROR_NONE";
+               break;
+       case SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER:
+               err_str = "SMARTTHINGS_RESOURCE_ERROR_INVALID_PARAMETER";
+               break;
+       case SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY:
+               err_str = "SMARTTHINGS_RESOURCE_ERROR_OUT_OF_MEMORY";
+               break;
+       case SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED:
+               err_str = "SMARTTHINGS_RESOURCE_ERROR_PERMISSION_DENIED";
+               break;
+       case SMARTTHINGS_RESOURCE_ERROR_NO_DATA:
+               err_str = "SMARTTHINGS_RESOURCE_ERROR_NO_DATA";
+               break;
+       case SMARTTHINGS_RESOURCE_ERROR_NOT_SUPPORTED:
+               err_str = "SMARTTHINGS_RESOURCE_ERROR_NOT_SUPPORTED";
+               break;
+       case SMARTTHINGS_RESOURCE_ERROR_OPERATION_FAILED:
+               err_str = "SMARTTHINGS_RESOURCE_ERROR_NOT_SUPPORTED";
+               break;
+       case SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE:
+               err_str = "SMARTTHINGS_RESOURCE_ERROR_SERVICE_UNAVAILABLE";
+               break;
+       default:
+               err_str = "Unknown error";
+               break;
+       }
+
+       return err_str;
+}
+
+/* get and set request handlers */
+static bool
+handle_get_motion(smartthings_payload_h resp_payload, void *user_data)
+{
+       int state = 0;
+
+       motion_state_get(&state);
+
+       _D("GET request for motion : %d", state);
+       smartthings_payload_set_bool(resp_payload, KEY_MOTION, state ? true : false);
+
+       return true;
+}
+
+static bool
+handle_set_motion(smartthings_payload_h payload,
+       smartthings_payload_h resp_payload, void *user_data)
+{
+       bool bvalue = 0;
+
+       if (smartthings_payload_get_bool(payload, KEY_MOTION, &bvalue) == 0) {
+               _D("requested volume: [%d]", bvalue);
+               motion_state_set((int)bvalue, RESOURCE_CALLBACK_KEY);
+               smartthings_payload_set_bool(resp_payload, KEY_MOTION, bvalue);
+       }
+
+       return true;
+}
+
+static bool
+handle_get_switch(smartthings_payload_h resp_payload, void *user_data)
+{
+       switch_state_e state = SWITCH_STATE_OFF;
+       const char *switch_state_name = NULL;
+
+       switch_state_get(&state);
+
+       if (state == SWITCH_STATE_OFF)
+               switch_state_name = VALUE_SWITCH_OFF;
+       else
+               switch_state_name = VALUE_SWITCH_ON;
+
+       _D("GET request for switch : %s", switch_state_name);
+       smartthings_payload_set_string(resp_payload, KEY_SWITCH, switch_state_name);
+
+       return true;
+}
+
+static bool
+handle_set_switch(smartthings_payload_h payload,
+       smartthings_payload_h resp_payload, void *user_data)
+{
+       char *str_value = NULL;
+       switch_state_e state = SWITCH_STATE_OFF;
+
+       if (smartthings_payload_get_string(payload, KEY_SWITCH, &str_value) != 0) {
+               _E("failed to smartthings_payload_get_string()");
+               return false;
+       }
+       retv_if(!str_value, false);
+
+       _D("SET request for switch: [%s]", str_value);
+
+       if (0 == g_strcmp0(str_value, VALUE_SWITCH_ON))
+               state = SWITCH_STATE_ON;
+
+       switch_state_set(state, RESOURCE_CALLBACK_KEY);
+
+       smartthings_payload_set_string(resp_payload, KEY_SWITCH, str_value);
+       free(str_value);
+
+       return true;
+}
+
+static bool
+handle_get_audiovolume(smartthings_payload_h resp_payload, void *user_data)
+{
+       double value = 0.0f;
+
+       servo_v_state_get(&value);
+
+       _D("current volume: [%f]", value);
+       smartthings_payload_set_int(resp_payload, KEY_VOLUME, (int)value);
+       smartthings_payload_set_bool(resp_payload, KEY_MUTE, false);
+
+       return true;
+}
+
+static bool
+handle_set_audiovolume(smartthings_payload_h payload,
+       smartthings_payload_h resp_payload, void *user_data)
+{
+       int ivalue = 0;
+
+       if (smartthings_payload_get_int(payload, KEY_VOLUME, &ivalue) == 0) {
+               _D("requested volume: [%d]", ivalue);
+               servo_v_state_set((double)ivalue, RESOURCE_CALLBACK_KEY);
+               smartthings_payload_set_int(resp_payload, KEY_VOLUME, ivalue);
+       }
+
+       smartthings_payload_set_bool(resp_payload, KEY_MUTE, false);
+
+       return true;
+}
+
+static bool
+handle_get_switchLevel(smartthings_payload_h resp_payload, void *user_data)
+{
+
+       double value = 0;
+
+       servo_h_state_get(&value);
+
+       _D("current switchLevel: [%f]", value);
+       smartthings_payload_set_int(resp_payload, KEY_SWITCHLEVEL, (int)value);
+
+       return true;
+
+}
+
+static bool
+handle_set_switchLevel(smartthings_payload_h payload,
+       smartthings_payload_h resp_payload, void *user_data)
+{
+       int ivalue = 0;
+
+       if (smartthings_payload_get_int(payload, KEY_SWITCHLEVEL, &ivalue) == 0) {
+               _D("requested dimming: [%d]", ivalue);
+               servo_h_state_set((double)ivalue, RESOURCE_CALLBACK_KEY);
+               smartthings_payload_set_int(resp_payload, KEY_SWITCHLEVEL, ivalue);
+       }
+
+       return true;
+}
+
+static void
+_request_cb(smartthings_resource_h handle, int req_id,
+       const char *uri, smartthings_resource_req_type_e req_type,
+       smartthings_payload_h payload, void *user_data)
+{
+       smartthings_payload_h resp_payload = NULL;
+       bool result = false;
+       int error = SMARTTHINGS_RESOURCE_ERROR_NONE;
+
+       _D("request on %s, type[%d], id[%d]", uri, req_type, req_id);
+
+       smartthings_payload_create(&resp_payload);
+       if (!resp_payload) {
+               _E("Response payload is NULL");
+               return;
+       }
+
+       if (req_type == SMARTTHINGS_RESOURCE_REQUEST_GET) {
+               if (0 == g_strcmp0(uri, URI_SWITCH))
+                       result = handle_get_switch(resp_payload, user_data);
+               else if (0 == g_strcmp0(uri, URI_AUDIOVOLUME))
+                       result = handle_get_audiovolume(resp_payload, user_data);
+               else if (0 == g_strcmp0(uri, URI_SWITCHLEVEL))
+                       result = handle_get_switchLevel(resp_payload, user_data);
+               else if (0 == g_strcmp0(uri, URI_MOTION))
+                       result = handle_get_motion(resp_payload, user_data);
+               else
+                       _E("No matching Resource uri");
+       } else if (req_type == SMARTTHINGS_RESOURCE_REQUEST_SET) {
+               if (0 == g_strcmp0(uri, URI_SWITCH))
+                       result = handle_set_switch(payload, resp_payload, user_data);
+               else if (0 == g_strcmp0(uri, URI_AUDIOVOLUME))
+                       result = handle_set_audiovolume(payload, resp_payload, user_data);
+               else if (0 == g_strcmp0(uri, URI_SWITCHLEVEL))
+                       result = handle_set_switchLevel(payload, resp_payload, user_data);
+               else if (0 == g_strcmp0(uri, URI_MOTION))
+                       result = handle_set_motion(payload, resp_payload, user_data);
+               else
+                       _E("No matching Resource uri");
+       } else {
+               _E("Invalid request type - %d", req_type);
+               smartthings_payload_destroy(resp_payload);
+               return;
+       }
+
+       error = smartthings_resource_send_response(handle, req_id, uri, resp_payload, result);
+       if (error != SMARTTHINGS_RESOURCE_ERROR_NONE) {
+                       smartthings_payload_destroy(resp_payload);
+                       _E("smartthings_resource_send_response() failed, [%s]",
+                               __resource_error_to_str(error));
+                       return;
+       }
+
+       if (req_type == SMARTTHINGS_RESOURCE_REQUEST_SET) {
+                       error = smartthings_resource_notify(handle, uri, resp_payload);
+                       if (error != SMARTTHINGS_RESOURCE_ERROR_NONE)
+                               _E("smartthings_resource_notify() failed, [%s]",
+                               __resource_error_to_str(error));
+       }
+
+       if (smartthings_payload_destroy(resp_payload))
+               _E("smartthings_payload_destroy failed");
+
+       return;
+}
+
+static void
+_resource_connection_status_cb(smartthings_resource_error_e error,
+       smartthings_resource_h handle,
+       smartthings_resource_connection_status_e status, void *user_data)
+{
+       _D("result [%s], status=[%d]", __resource_error_to_str(error), status);
+
+       g_conn_status = status;
+
+       if (status == SMARTTHINGS_RESOURCE_CONNECTION_STATUS_CONNECTED) {
+               if (smartthings_resource_set_request_cb(handle, _request_cb, NULL)) {
+                       _E("smartthings_resource_set_request_cb() is failed");
+                       return;
+               }
+       } else {
+               _E("connection failed");
+       }
+       return;
+}
+
+static void __motion_changed(int value, void* user_data)
+{
+       smartthings_resource_h handle = user_data;
+       smartthings_payload_h payload = NULL;
+       int error = SMARTTHINGS_RESOURCE_ERROR_NONE;
+
+       ret_if(!handle);
+
+       _D("motion changed to - %d", value);
+
+       ret_if(g_conn_status != SMARTTHINGS_RESOURCE_CONNECTION_STATUS_CONNECTED);
+
+       smartthings_payload_create(&payload);
+       if (!payload) {
+               _E("failed to create payload is NULL");
+               return;
+       }
+
+       smartthings_payload_set_bool(payload, KEY_MOTION, value ? true : false);
+
+       error = smartthings_resource_notify(handle, URI_MOTION, payload);
+       if (error != SMARTTHINGS_RESOURCE_ERROR_NONE)
+               _E("smartthings_resource_notify() failed, [%s]",
+                       __resource_error_to_str(error));
+
+       smartthings_payload_destroy(payload);
+}
+
+static void __switch_changed(switch_state_e state, void* user_data)
+{
+       smartthings_resource_h handle = user_data;
+       smartthings_payload_h payload = NULL;
+       int error = SMARTTHINGS_RESOURCE_ERROR_NONE;
+       const char *switch_state_name = NULL;
+
+       _D("switch changed to - %d", state);
+       ret_if(!handle);
+       ret_if(g_conn_status != SMARTTHINGS_RESOURCE_CONNECTION_STATUS_CONNECTED);
+
+       smartthings_payload_create(&payload);
+       if (!payload) {
+               _E("failed to create payload is NULL");
+               return;
+       }
+
+       if (state == SWITCH_STATE_OFF)
+               switch_state_name = VALUE_SWITCH_OFF;
+       else
+               switch_state_name = VALUE_SWITCH_ON;
+
+       smartthings_payload_set_string(payload, KEY_SWITCH, switch_state_name);
+
+       error = smartthings_resource_notify(handle, URI_SWITCH, payload);
+       if (error != SMARTTHINGS_RESOURCE_ERROR_NONE)
+               _E("smartthings_resource_notify() failed, [%s]",
+                       __resource_error_to_str(error));
+
+       smartthings_payload_destroy(payload);
+}
+
+static void __servo_v_changed(double value, void* user_data)
+{
+       smartthings_resource_h handle = user_data;
+       smartthings_payload_h payload = NULL;
+       int error = SMARTTHINGS_RESOURCE_ERROR_NONE;
+
+       _D("servo_v changed to - %f", value);
+       ret_if(!handle);
+       ret_if(g_conn_status != SMARTTHINGS_RESOURCE_CONNECTION_STATUS_CONNECTED);
+
+       smartthings_payload_create(&payload);
+       if (!payload) {
+               _E("failed to create payload is NULL");
+               return;
+       }
+
+       smartthings_payload_set_int(payload, KEY_VOLUME, (int)value);
+
+       error = smartthings_resource_notify(handle, URI_AUDIOVOLUME, payload);
+       if (error != SMARTTHINGS_RESOURCE_ERROR_NONE)
+               _E("smartthings_resource_notify() failed, [%s]",
+                       __resource_error_to_str(error));
+
+       smartthings_payload_destroy(payload);
+}
+
+static void __servo_h_changed(double value, void* user_data)
+{
+       smartthings_resource_h handle = user_data;
+       smartthings_payload_h payload = NULL;
+       int error = SMARTTHINGS_RESOURCE_ERROR_NONE;
+
+       _D("servo_h changed to - %f", value);
+       ret_if(!handle);
+       ret_if(g_conn_status != SMARTTHINGS_RESOURCE_CONNECTION_STATUS_CONNECTED);
+
+       smartthings_payload_create(&payload);
+       if (!payload) {
+               _E("failed to create payload is NULL");
+               return;
+       }
+
+       smartthings_payload_set_int(payload, KEY_SWITCHLEVEL, (int)value);
+
+       error = smartthings_resource_notify(handle, URI_SWITCHLEVEL, payload);
+       if (error != SMARTTHINGS_RESOURCE_ERROR_NONE)
+               _E("smartthings_resource_notify() failed, [%s]",
+                       __resource_error_to_str(error));
+
+       smartthings_payload_destroy(payload);
+}
+
+static int __device_interfaces_set_callbak(smartthings_resource_h handle)
+{
+       retv_if(!handle, -1);
+
+       // Assume that, device interfaces are already initialized in controller.c
+       switch_state_changed_cb_set(RESOURCE_CALLBACK_KEY,
+               __switch_changed, handle);
+       servo_v_state_changed_cb_set(RESOURCE_CALLBACK_KEY,
+               __servo_v_changed, handle);
+       servo_h_state_changed_cb_set(RESOURCE_CALLBACK_KEY,
+               __servo_h_changed, handle);
+       motion_state_changed_cb_set(RESOURCE_CALLBACK_KEY,
+               __motion_changed, handle);
+
+       return 0;
+}
+
+
+int st_thing_resource_init(void)
+{
+       smartthings_resource_h st_res_h = NULL;
+       int error = 0;
+
+       if (g_st_res_h) {
+               _I("Already initialized!");
+               return 0;
+       }
+
+       error = smartthings_resource_initialize(&st_res_h,
+                               _resource_connection_status_cb, NULL);
+       if (error) {
+               _E("smartthings_resource_initialize() is failed, [%s]",
+                       __resource_error_to_str(error));
+               return -1;
+       }
+
+       g_st_res_h = st_res_h;
+       g_conn_status = SMARTTHINGS_RESOURCE_CONNECTION_STATUS_DISCONNECTED;
+
+       __device_interfaces_set_callbak(g_st_res_h);
+
+       return 0;
+}
+
+int st_thing_resource_fini(void)
+{
+       if (!g_st_res_h)
+               return 0;
+
+       smartthings_resource_unset_request_cb(g_st_res_h);
+       smartthings_resource_deinitialize(g_st_res_h);
+       g_st_res_h = NULL;
+       g_conn_status = SMARTTHINGS_RESOURCE_CONNECTION_STATUS_DISCONNECTED;
+
+       return 0;
+}
diff --git a/src/switch.c b/src/switch.c
new file mode 100644 (file)
index 0000000..297d66c
--- /dev/null
@@ -0,0 +1,181 @@
+ /*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <glib.h>
+#include "log.h"
+#include "switch.h"
+
+#define IDLE_PRIORITY (G_PRIORITY_HIGH_IDLE + 30)
+
+struct switch_data_s {
+       switch_state_e state;
+       GHashTable *callback_hash;
+};
+
+struct switch_callback_s {
+       switch_state_changed_cb callback;
+       void *cb_data;
+};
+
+struct switch_pass_data_s {
+       char *pass_key;
+       switch_state_e state;
+};
+
+static struct switch_data_s *g_switch;
+static pthread_mutex_t g_switch_mutex;
+
+static int __free_switch_handle(struct switch_data_s *sw)
+{
+       if (sw->callback_hash) {
+               g_hash_table_destroy(sw->callback_hash);
+               g_hash_table_unref(sw->callback_hash);
+       }
+
+       free(sw);
+       return 0;
+}
+
+int switch_initialize(void)
+{
+       if (g_switch) {
+               _D("The switch is already initialized!");
+               return 0;
+       }
+
+       g_switch = malloc(sizeof(struct switch_data_s));
+       retv_if(!g_switch, -1);
+
+       g_switch->callback_hash =
+               g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+       pthread_mutex_init(&g_switch_mutex, NULL);
+       g_switch->state = SWITCH_STATE_OFF;
+
+       /* TODO : initialize real switch here */
+
+       return 0;
+}
+
+int switch_finalize(void)
+{
+       if (g_switch) {
+               pthread_mutex_destroy(&g_switch_mutex);
+               __free_switch_handle(g_switch);
+               g_switch = NULL;
+       }
+
+       return 0;
+}
+
+static void __cb_item_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+       char *item_name = key;
+       struct switch_callback_s *cb_item = value;
+       struct switch_pass_data_s *pass_item = user_data;
+
+       ret_if(!item_name);
+       ret_if(!cb_item);
+       ret_if(!pass_item);
+
+
+       if (pass_item->pass_key &&
+               (0 == g_strcmp0(item_name, pass_item->pass_key))) {
+                       // _D("pass item - %s", item_name);
+                       return;
+               }
+
+       _D("callback called for [%s]", item_name);
+       cb_item->callback(pass_item->state, cb_item->cb_data);
+}
+
+static gboolean __call_cb_idle(gpointer data)
+{
+       struct switch_pass_data_s *pass_item = data;
+
+       g_hash_table_foreach(g_switch->callback_hash, __cb_item_foreach, pass_item);
+       g_free(pass_item->pass_key);
+       g_free(pass_item);
+
+       return G_SOURCE_REMOVE;
+}
+
+int switch_state_set(switch_state_e state, const char *pass_key)
+{
+       int old_state = 0;
+       retv_if(!g_switch, -1);
+
+       pthread_mutex_lock(&g_switch_mutex);
+       old_state = g_switch->state;
+       pthread_mutex_unlock(&g_switch_mutex);
+
+       if (old_state != state) {
+               pthread_mutex_lock(&g_switch_mutex);
+               g_switch->state = state;
+               pthread_mutex_unlock(&g_switch_mutex);
+
+               if (g_hash_table_size(g_switch->callback_hash) > 0) {
+                       struct switch_pass_data_s *pass_item = NULL;
+
+                       pass_item = g_new(struct switch_pass_data_s, 1);
+                       pass_item->pass_key = g_strdup(pass_key);
+                       pass_item->state = state;
+                       g_idle_add_full(IDLE_PRIORITY,
+                               __call_cb_idle, pass_item, NULL);
+               }
+       }
+       return 0;
+}
+
+int switch_state_get(switch_state_e *state)
+{
+       retv_if(!g_switch, -1);
+       retv_if(!state, -1);
+
+       pthread_mutex_lock(&g_switch_mutex);
+       *state = g_switch->state;
+       pthread_mutex_unlock(&g_switch_mutex);
+
+       // _D("get state : %d", *state);
+
+       return 0;
+}
+
+int switch_state_changed_cb_set(
+       const char *callback_key, switch_state_changed_cb callback, void *cb_data)
+{
+       retv_if(!g_switch, -1);
+       retv_if(!g_switch->callback_hash, -1);
+       retv_if(!callback_key, -1);
+
+       if (callback) {
+               struct switch_callback_s *cb_item = NULL;
+               cb_item = g_try_new(struct switch_callback_s, 1);
+               retv_if(!cb_item, -1);
+
+               cb_item->callback = callback;
+               cb_item->cb_data = cb_data;
+
+               g_hash_table_insert(g_switch->callback_hash,
+                       g_strdup(callback_key), cb_item);
+       } else {
+               g_hash_table_remove(g_switch->callback_hash, callback_key);
+       }
+
+       return 0;
+}
diff --git a/tizen-manifest.xml.in b/tizen-manifest.xml.in
new file mode 100644 (file)
index 0000000..fa96d83
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="5.0" package="@PROJECT_NAME@" version="1.0.0">
+<profile name="iot-headless"/>
+<service-application appid="@PROJECT_NAME@" auto-restart="true" exec="@PROJECT_NAME@" multiple="false" nodisplay="true" on-boot="true" taskmanage="false" type="capp">
+       <label>@PROJECT_NAME@</label>
+       <icon>@PROJECT_NAME@.png</icon>
+       <metadata key="http://tizen.org/iot/metadata/master" value="shared/res/master.json"/>
+       <metadata key="http://tizen.org/iot/metadata/resource" value="shared/res/resource.json"/>
+       <metadata key="samsung-experience-service-api-level" value="1"/>
+       <background-category value="background-network"/>
+       <background-category value="iot-communication"/>
+</service-application>
+       <privileges>
+               <privilege>http://tizen.org/privilege/camera</privilege>
+               <privilege>http://tizen.org/privilege/peripheralio</privilege>
+               <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+               <privilege>http://tizen.org/privilege/datasharing</privilege>
+               <privilege>http://tizen.org/privilege/internet</privilege>
+               <privilege>http://tizen.org/privilege/softap</privilege>
+               <appdefined-privilege>http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.master</appdefined-privilege>
+               <appdefined-privilege>http://com.samsung.tizen.smartthings-thing/appdefined/smartthings-thing.resource</appdefined-privilege>
+       </privileges>
+</manifest>
diff --git a/update-dashboard.sh b/update-dashboard.sh
new file mode 100755 (executable)
index 0000000..b6fee32
--- /dev/null
@@ -0,0 +1,24 @@
+cd dashboard
+
+sdb root on;
+sdb shell 'mount -o remount,rw /';
+
+sdb shell 'systemctl stop iot-dashboard'
+
+sdb shell 'rm -rf /opt/home/dashboard'
+sdb shell 'mkdir -p /opt/home/dashboard/public/js';
+sdb shell 'mkdir -p /opt/home/dashboard/public/css';
+
+sdb push server.js           /opt/home/dashboard;
+sdb push default.gif          /opt/home/dashboard;
+sdb push public/index.html    /opt/home/dashboard/public/index.html;
+sdb push public/test.html     /opt/home/dashboard/public/test.html;
+sdb push public/js/app.js     /opt/home/dashboard/public/js/app.js;
+sdb push public/css/style.css /opt/home/dashboard/public/css/style.css;
+
+sdb shell 'cd /opt/home/dashboard; find .';
+
+# sdb shell 'cd /opt/home/dashboard; iotjs server.js'
+
+sdb shell 'systemctl start iot-dashboard'
+sdb shell 'systemctl status iot-dashboard'