From d5a56c462040801257672deb2e4363f85f576dd0 Mon Sep 17 00:00:00 2001 From: charitha Date: Mon, 27 Jun 2016 09:55:37 +0530 Subject: [PATCH] Add Smart Water Tank device type --- .../watertank/component/analytics/build.xml | 42 + .../watertank/component/analytics/pom.xml | 86 ++ .../component/analytics/src/assembly/src.xml | 36 + .../carbonapps/relay_sensor/artifacts.xml | 28 + .../relay_sensor/relay_publisher/artifact.xml | 22 + .../relay_publisher/relay_publisher.xml | 26 + .../relay_sensor/relay_receiver/artifact.xml | 22 + .../relay_receiver/relay_receiver.xml | 26 + .../relay_sensor/relay_script/artifact.xml | 22 + .../relay_script/relay_script.xml | 34 + .../relay_sensor/relay_store/artifact.xml | 22 + .../org_wso2_iot_devices_relay.xml | 62 + .../relay_sensor/relay_stream/artifact.xml | 23 + .../org.wso2.iot.devices.relay_1.0.0.json | 33 + .../waterlevel_sensor/artifacts.xml | 27 + .../waterlevel_receiver/artifact.xml | 22 + .../waterlevel_receiver.xml | 27 + .../waterlevel_script/artifact.xml | 22 + .../waterlevel_script/waterlevel_script.xml | 35 + .../waterlevel_store/artifact.xml | 22 + .../org_wso2_iot_devices_waterlevel.xml | 62 + .../waterlevel_stream/artifact.xml | 23 + ...org.wso2.iot.devices.waterlevel_1.0.0.json | 33 + .../carbonapps/watertank/artifacts.xml | 27 + .../watertank_execution/artifact.xml | 23 + .../watertank_execution.siddhiql | 24 + .../watertank_publisher/artifact.xml | 22 + .../watertank_publisher.xml | 26 + .../watertank/watertank_receiver/artifact.xml | 22 + .../watertank_receiver/firealarm_receiver.xml | 38 + .../watertank/watertank_stream/artifact.xml | 23 + .../org.wso2.iot.watertank_1.0.0.json | 26 + .../samples/watertank/component/api/pom.xml | 168 +++ .../watertank/api/DeviceTypeService.java | 119 ++ .../watertank/api/DeviceTypeServiceImpl.java | 434 ++++++ .../watertank/api/dto/DeviceJSON.java | 37 + .../watertank/api/dto/SensorRecord.java | 88 ++ .../api/exception/DeviceTypeException.java | 57 + .../watertank/api/util/APIUtil.java | 221 +++ .../watertank/api/util/Constants.java | 32 + .../watertank/api/util/ZipUtil.java | 91 ++ .../src/main/webapp/META-INF/permissions.xml | 87 ++ .../webapp/META-INF/webapp-classloading.xml | 33 + .../src/main/webapp/WEB-INF/cxf-servlet.xml | 36 + .../api/src/main/webapp/WEB-INF/web.xml | 46 + .../watertank/component/plugin/pom.xml | 133 ++ .../plugin/constants/DeviceTypeConstants.java | 63 + .../exception/DeviceMgtPluginException.java | 56 + .../plugin/impl/DeviceTypeManager.java | 257 ++++ .../plugin/impl/DeviceTypeManagerService.java | 62 + .../plugin/impl/dao/DeviceTypeDAO.java | 129 ++ .../impl/dao/impl/DeviceTypeDAOImpl.java | 204 +++ .../feature/DeviceTypeFeatureManager.java | 58 + .../impl/util/DeviceSchemaInitializer.java | 49 + .../impl/util/DeviceTypeStartupListener.java | 42 + .../plugin/impl/util/DeviceTypeUtils.java | 189 +++ .../DeviceTypeManagementDataHolder.java | 47 + .../plugin/internal/ServiceComponent.java | 113 ++ .../watertank/plugin/mqtt/MqttConfig.java | 60 + modules/samples/watertank/component/pom.xml | 56 + .../samples/watertank/component/ui/pom.xml | 56 + .../component/ui/src/assembly/src.xml | 37 + .../analytics-view.hbs | 50 + .../analytics-view.js | 42 + .../analytics-view.json | 3 + .../public/js/watertank.js | 228 +++ .../device-view.hbs | 68 + .../device-view.js | 41 + .../device-view.json | 3 + .../public/images/deviceType.png | Bin 0 -> 28584 bytes .../analytics-view.hbs | 48 + .../analytics-view.js | 32 + .../analytics-view.json | 3 + .../public/js/device-stats.js | 128 ++ .../public/js/moment.min.js | 25 + .../public/js/socket.io.min.js | 2 + .../private/config.json | 7 + .../public/images/deviceType.png | Bin 0 -> 28584 bytes .../public/images/myDevices_analytics.png | Bin 0 -> 6693 bytes .../public/images/schematicsGuide.png | Bin 0 -> 103037 bytes .../public/images/thumb.png | Bin 0 -> 31021 bytes .../public/js/download.js | 183 +++ .../public/js/jquery.validate.js | 1234 +++++++++++++++++ .../type-view.hbs | 372 +++++ .../type-view.json | 3 + .../samples/watertank/feature/feature/pom.xml | 207 +++ .../feature/src/main/resources/agent/init.lua | 4 + .../main/resources/agent/sketch.properties | 16 + .../src/main/resources/agent/water-tank.lua | 76 + .../src/main/resources/agent/wifi-connect.lua | 2 + .../src/main/resources/build.properties | 18 + .../src/main/resources/configs/watertank.xml | 24 + .../datasources/watertank-datasources.xml | 47 + .../src/main/resources/dbscripts/h2.sql | 11 + .../src/main/resources/dbscripts/mssql.sql | 9 + .../src/main/resources/dbscripts/mysql.sql | 10 + .../src/main/resources/dbscripts/oracle.sql | 9 + .../feature/feature/src/main/resources/p2.inf | 28 + modules/samples/watertank/feature/pom.xml | 34 + modules/samples/watertank/pom.xml | 418 ++++++ 100 files changed, 7433 insertions(+) create mode 100644 modules/samples/watertank/component/analytics/build.xml create mode 100644 modules/samples/watertank/component/analytics/pom.xml create mode 100644 modules/samples/watertank/component/analytics/src/assembly/src.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/artifacts.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_publisher/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_publisher/relay_publisher.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_receiver/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_receiver/relay_receiver.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_script/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_script/relay_script.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_store/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_store/org_wso2_iot_devices_relay.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_stream/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_stream/org.wso2.iot.devices.relay_1.0.0.json create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/artifacts.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_receiver/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_receiver/waterlevel_receiver.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_script/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_script/waterlevel_script.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_store/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_store/org_wso2_iot_devices_waterlevel.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_stream/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_stream/org.wso2.iot.devices.waterlevel_1.0.0.json create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/artifacts.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_execution/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_execution/watertank_execution.siddhiql create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_publisher/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_publisher/watertank_publisher.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_receiver/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_receiver/firealarm_receiver.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_stream/artifact.xml create mode 100644 modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_stream/org.wso2.iot.watertank_1.0.0.json create mode 100644 modules/samples/watertank/component/api/pom.xml create mode 100644 modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/DeviceTypeService.java create mode 100644 modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/DeviceTypeServiceImpl.java create mode 100644 modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/dto/DeviceJSON.java create mode 100644 modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/dto/SensorRecord.java create mode 100644 modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/exception/DeviceTypeException.java create mode 100644 modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/APIUtil.java create mode 100644 modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/Constants.java create mode 100644 modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/ZipUtil.java create mode 100644 modules/samples/watertank/component/api/src/main/webapp/META-INF/permissions.xml create mode 100644 modules/samples/watertank/component/api/src/main/webapp/META-INF/webapp-classloading.xml create mode 100644 modules/samples/watertank/component/api/src/main/webapp/WEB-INF/cxf-servlet.xml create mode 100644 modules/samples/watertank/component/api/src/main/webapp/WEB-INF/web.xml create mode 100644 modules/samples/watertank/component/plugin/pom.xml create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/constants/DeviceTypeConstants.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/exception/DeviceMgtPluginException.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/DeviceTypeManager.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/DeviceTypeManagerService.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/dao/DeviceTypeDAO.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/dao/impl/DeviceTypeDAOImpl.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/feature/DeviceTypeFeatureManager.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceSchemaInitializer.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceTypeStartupListener.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceTypeUtils.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/internal/DeviceTypeManagementDataHolder.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/internal/ServiceComponent.java create mode 100644 modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/mqtt/MqttConfig.java create mode 100644 modules/samples/watertank/component/pom.xml create mode 100644 modules/samples/watertank/component/ui/pom.xml create mode 100644 modules/samples/watertank/component/ui/src/assembly/src.xml create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.hbs create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.json create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/public/js/watertank.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.hbs create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.json create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/public/images/deviceType.png create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.hbs create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.json create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/device-stats.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/moment.min.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/socket.io.min.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/private/config.json create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/public/images/deviceType.png create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/public/images/myDevices_analytics.png create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/public/images/schematicsGuide.png create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/public/images/thumb.png create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/public/js/download.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/public/js/jquery.validate.js create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/type-view.hbs create mode 100644 modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.type-view/type-view.json create mode 100644 modules/samples/watertank/feature/feature/pom.xml create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/agent/init.lua create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/agent/sketch.properties create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/agent/water-tank.lua create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/agent/wifi-connect.lua create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/build.properties create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/configs/watertank.xml create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/datasources/watertank-datasources.xml create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/dbscripts/h2.sql create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/dbscripts/mssql.sql create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/dbscripts/mysql.sql create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/dbscripts/oracle.sql create mode 100644 modules/samples/watertank/feature/feature/src/main/resources/p2.inf create mode 100644 modules/samples/watertank/feature/pom.xml create mode 100644 modules/samples/watertank/pom.xml diff --git a/modules/samples/watertank/component/analytics/build.xml b/modules/samples/watertank/component/analytics/build.xml new file mode 100644 index 00000000..28ff77c7 --- /dev/null +++ b/modules/samples/watertank/component/analytics/build.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/samples/watertank/component/analytics/pom.xml b/modules/samples/watertank/component/analytics/pom.xml new file mode 100644 index 00000000..bf13fe6f --- /dev/null +++ b/modules/samples/watertank/component/analytics/pom.xml @@ -0,0 +1,86 @@ + + + + org.homeautomation + watertank-component + 1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + ${groupId}.watertank.analytics + 1.0-SNAPSHOT + pom + ${groupId}.watertank.analytics + http://wso2.org + + + + maven-clean-plugin + ${maven-clean-plugin.version} + + + auto-clean + initialize + + clean + + + + + + maven-antrun-plugin + ${wso2.maven.compiler.source} + + + process-resources + + + + + + + run + + + + + + maven-assembly-plugin + ${maven-assembly-plugin.version} + + ${project.artifactId}-1.0-SNAPSHOT + false + + src/assembly/src.xml + + + + + create-archive + package + + single + + + + + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/analytics/src/assembly/src.xml b/modules/samples/watertank/component/analytics/src/assembly/src.xml new file mode 100644 index 00000000..b7735b0b --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/assembly/src.xml @@ -0,0 +1,36 @@ + + + + src + + zip + + false + ${basedir}/src + + + ${basedir}/target/carbonapps + / + true + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/artifacts.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/artifacts.xml new file mode 100644 index 00000000..dc71c7b5 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/artifacts.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_publisher/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_publisher/artifact.xml new file mode 100644 index 00000000..bcc49889 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_publisher/artifact.xml @@ -0,0 +1,22 @@ + + + + + relay_publisher.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_publisher/relay_publisher.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_publisher/relay_publisher.xml new file mode 100644 index 00000000..d4c6e704 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_publisher/relay_publisher.xml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_receiver/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_receiver/artifact.xml new file mode 100644 index 00000000..98cf9a57 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_receiver/artifact.xml @@ -0,0 +1,22 @@ + + + + + relay_receiver.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_receiver/relay_receiver.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_receiver/relay_receiver.xml new file mode 100644 index 00000000..f1bd9114 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_receiver/relay_receiver.xml @@ -0,0 +1,26 @@ + + + + + + false + + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_script/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_script/artifact.xml new file mode 100644 index 00000000..85799189 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_script/artifact.xml @@ -0,0 +1,22 @@ + + + + + relay_script.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_script/relay_script.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_script/relay_script.xml new file mode 100644 index 00000000..9b2eeb5c --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_script/relay_script.xml @@ -0,0 +1,34 @@ + + + + + relay_script + + 0 0/5 * * * ? + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_store/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_store/artifact.xml new file mode 100644 index 00000000..819e539e --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_store/artifact.xml @@ -0,0 +1,22 @@ + + + + + org_wso2_iot_devices_relay.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_store/org_wso2_iot_devices_relay.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_store/org_wso2_iot_devices_relay.xml new file mode 100644 index 00000000..74f2f7c3 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_store/org_wso2_iot_devices_relay.xml @@ -0,0 +1,62 @@ + + + + + + org.wso2.iot.devices.relay:1.0.0 + + EVENT_STORE + + + meta_owner + true + true + false + STRING + + + meta_deviceType + true + true + false + STRING + + + meta_deviceId + true + true + false + STRING + + + meta_time + true + true + false + LONG + + + relay + false + false + false + BOOLEAN + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_stream/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_stream/artifact.xml new file mode 100644 index 00000000..389fd6b3 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_stream/artifact.xml @@ -0,0 +1,23 @@ + + + + + org.wso2.iot.devices.relay_1.0.0.json + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_stream/org.wso2.iot.devices.relay_1.0.0.json b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_stream/org.wso2.iot.devices.relay_1.0.0.json new file mode 100644 index 00000000..579e9c45 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/relay_sensor/relay_stream/org.wso2.iot.devices.relay_1.0.0.json @@ -0,0 +1,33 @@ +{ + "name": "org.wso2.iot.devices.relay", + "version": "1.0.0", + "nickName": "Water Pump / Valve Relay", + "description": "Status of the relay which used to control water pump or valve", + "metaData": [ + { + "name": "owner", + "type": "STRING" + }, + { + "name": "deviceType", + "type": "STRING" + }, + { + "name": "deviceId", + "type": "STRING" + }, + { + "name": "time", + "type": "LONG" + } + ], + "payloadData": [ + { + "name": "relay", + "type": "BOOLEAN" + } + ] +} + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/artifacts.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/artifacts.xml new file mode 100644 index 00000000..62eb4f97 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/artifacts.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_receiver/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_receiver/artifact.xml new file mode 100644 index 00000000..9bd042f3 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_receiver/artifact.xml @@ -0,0 +1,22 @@ + + + + + waterlevel_receiver.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_receiver/waterlevel_receiver.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_receiver/waterlevel_receiver.xml new file mode 100644 index 00000000..30b9761a --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_receiver/waterlevel_receiver.xml @@ -0,0 +1,27 @@ + + + + + + false + + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_script/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_script/artifact.xml new file mode 100644 index 00000000..e4529006 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_script/artifact.xml @@ -0,0 +1,22 @@ + + + + + waterlevel_script.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_script/waterlevel_script.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_script/waterlevel_script.xml new file mode 100644 index 00000000..4f7d1f11 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_script/waterlevel_script.xml @@ -0,0 +1,35 @@ + + + + + waterlevel_script + + 0 0/5 * * * ? + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_store/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_store/artifact.xml new file mode 100644 index 00000000..8914cc99 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_store/artifact.xml @@ -0,0 +1,22 @@ + + + + + org_wso2_iot_devices_waterlevel.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_store/org_wso2_iot_devices_waterlevel.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_store/org_wso2_iot_devices_waterlevel.xml new file mode 100644 index 00000000..f413f99b --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_store/org_wso2_iot_devices_waterlevel.xml @@ -0,0 +1,62 @@ + + + + + + org.wso2.iot.devices.waterlevel:1.0.0 + + EVENT_STORE + + + meta_owner + true + true + false + STRING + + + meta_deviceType + true + true + false + STRING + + + meta_deviceId + true + true + false + STRING + + + meta_time + true + true + false + LONG + + + waterlevel + false + false + false + FLOAT + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_stream/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_stream/artifact.xml new file mode 100644 index 00000000..f07f6722 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_stream/artifact.xml @@ -0,0 +1,23 @@ + + + + + org.wso2.iot.devices.waterlevel_1.0.0.json + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_stream/org.wso2.iot.devices.waterlevel_1.0.0.json b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_stream/org.wso2.iot.devices.waterlevel_1.0.0.json new file mode 100644 index 00000000..954e05b8 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/waterlevel_sensor/waterlevel_stream/org.wso2.iot.devices.waterlevel_1.0.0.json @@ -0,0 +1,33 @@ +{ + "name": "org.wso2.iot.devices.waterlevel", + "version": "1.0.0", + "nickName": "Water Level Data", + "description": "Water Level data received from the Device", + "metaData": [ + { + "name": "owner", + "type": "STRING" + }, + { + "name": "deviceType", + "type": "STRING" + }, + { + "name": "deviceId", + "type": "STRING" + }, + { + "name": "time", + "type": "LONG" + } + ], + "payloadData": [ + { + "name": "waterlevel", + "type": "FLOAT" + } + ] +} + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/artifacts.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/artifacts.xml new file mode 100644 index 00000000..96b53ae7 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/artifacts.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_execution/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_execution/artifact.xml new file mode 100644 index 00000000..8f56540a --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_execution/artifact.xml @@ -0,0 +1,23 @@ + + + + + watertank_execution.siddhiql + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_execution/watertank_execution.siddhiql b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_execution/watertank_execution.siddhiql new file mode 100644 index 00000000..75be22f3 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_execution/watertank_execution.siddhiql @@ -0,0 +1,24 @@ +/* Enter a unique ExecutionPlan */ +@Plan:name('watertank_execution') + +/* Enter a unique description for ExecutionPlan */ +-- @Plan:description('watertank_execution') + +/* define streams/tables and write queries here ... */ + +@Import('org.wso2.iot.watertank:1.0.0') +define stream watertank (meta_owner string, meta_deviceId string, relay float, waterlevel float); + +@Export('org.wso2.iot.devices.relay:1.0.0') +define stream relay (meta_owner string, meta_deviceType string, meta_deviceId string, meta_time long, relay float); + +@Export('org.wso2.iot.devices.waterlevel:1.0.0') +define stream waterlevel (meta_owner string, meta_deviceType string, meta_deviceId string, meta_time long, waterlevel float); + +from watertank +select meta_owner, 'watertank' as meta_deviceType, meta_deviceId, time:timestampInMilliseconds() as meta_time, relay +insert into relay; + +from watertank +select meta_owner, 'watertank' as meta_deviceType, meta_deviceId, time:timestampInMilliseconds() as meta_time, waterlevel +insert into waterlevel; \ No newline at end of file diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_publisher/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_publisher/artifact.xml new file mode 100644 index 00000000..467d3b33 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_publisher/artifact.xml @@ -0,0 +1,22 @@ + + + + + watertank_publisher.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_publisher/watertank_publisher.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_publisher/watertank_publisher.xml new file mode 100644 index 00000000..45e15756 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_publisher/watertank_publisher.xml @@ -0,0 +1,26 @@ + + + + + + + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_receiver/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_receiver/artifact.xml new file mode 100644 index 00000000..ab496171 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_receiver/artifact.xml @@ -0,0 +1,22 @@ + + + + + watertank_receiver.xml + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_receiver/firealarm_receiver.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_receiver/firealarm_receiver.xml new file mode 100644 index 00000000..df75155b --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_receiver/firealarm_receiver.xml @@ -0,0 +1,38 @@ + + + + + + carbon.super/watertank/+/data + admin + + device_id_json_path:event.metaData.deviceId,device_id_topic_hierarchy_index:2 + + org.wso2.carbon.device.mgt.iot.input.adapter.mqtt.util.MQTTContentValidator + + default + https://localhost:${carbon.https.port}/dynamic-client-web/register + tcp://${mqtt.broker.host}:${mqtt.broker.port} + true + + + + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_stream/artifact.xml b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_stream/artifact.xml new file mode 100644 index 00000000..cb98d649 --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_stream/artifact.xml @@ -0,0 +1,23 @@ + + + + + org.wso2.iot.watertank_1.0.0.json + + diff --git a/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_stream/org.wso2.iot.watertank_1.0.0.json b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_stream/org.wso2.iot.watertank_1.0.0.json new file mode 100644 index 00000000..f707afbd --- /dev/null +++ b/modules/samples/watertank/component/analytics/src/main/resources/carbonapps/watertank/watertank_stream/org.wso2.iot.watertank_1.0.0.json @@ -0,0 +1,26 @@ +{ + "name": "org.wso2.iot.watertank", + "version": "1.0.0", + "nickName": "Water Tank stream", + "description": "This hold the device type stream of water tank", + "metaData": [ + { + "name": "owner", + "type": "STRING" + }, + { + "name": "deviceId", + "type": "STRING" + } + ], + "payloadData": [ + { + "name": "relay", + "type": "BOOLEAN" + }, + { + "name": "waterlevel", + "type": "FLOAT" + } + ] +} \ No newline at end of file diff --git a/modules/samples/watertank/component/api/pom.xml b/modules/samples/watertank/component/api/pom.xml new file mode 100644 index 00000000..c3d56db6 --- /dev/null +++ b/modules/samples/watertank/component/api/pom.xml @@ -0,0 +1,168 @@ + + + + + + org.homeautomation + watertank-component + 1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + ${project-base-package}.api + 1.0-SNAPSHOT + war + ${project-base-package}.api + http://wso2.com + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + UTF-8 + ${maven.compiler.source} + ${maven.compiler.target} + + + + maven-war-plugin + ${maven-war-plugin.version} + + ${project-base-package}.api + + + + + + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + provided + + + org.apache.axis2.wso2 + axis2-client + + + + + + + org.apache.cxf + cxf-rt-frontend-jaxws + provided + + + org.apache.cxf + cxf-rt-frontend-jaxrs + provided + + + org.apache.cxf + cxf-rt-transports-http + provided + + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + provided + + + + + org.apache.httpcomponents + httpasyncclient + 4.1 + provided + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.iot + provided + + + org.homeautomation + ${project-base-package}.plugin + provided + + + + + org.codehaus.jackson + jackson-core-asl + + + org.codehaus.jackson + jackson-jaxrs + + + javax + javaee-web-api + provided + + + javax.ws.rs + jsr311-api + provided + + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.analytics.data.publisher + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.apimgt.annotations + provided + + + org.wso2.carbon.analytics + org.wso2.carbon.analytics.api + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.extensions + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.apimgt.application.extension + provided + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/DeviceTypeService.java b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/DeviceTypeService.java new file mode 100644 index 00000000..553ade61 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/DeviceTypeService.java @@ -0,0 +1,119 @@ +/* +* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +* +* WSO2 Inc. licenses this file to you 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. +*/ + +package org.homeautomation.watertank.api; + +import org.homeautomation.watertank.api.dto.DeviceJSON; + +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType; +import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Path; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Produces; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.PUT; +import javax.ws.rs.DELETE; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + + +/** + * This is the controller API which is used to control agent side functionality + */ +@SuppressWarnings("NonJaxWsWebServices") +@API(name = "watertank", version = "1.0.0", context = "/watertank", tags = "watertank") +@DeviceType(value = "watertank") +interface DeviceTypeService { + + /** + * @param agentInfo device owner,id and sensor value + */ + @Path("device/register") + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Permission(scope = "watertank_user", permissions = {"/permission/admin/device-mgt/user/register"}) + Response registerDevice(final DeviceJSON agentInfo); + + /** + * @param deviceId unique identifier for given device type + * @param onLevel level to turn on the relay + * @param offLevel level to turn off thr relay + */ + @Path("device/{deviceId}/change-levels") + @POST + @Feature(code = "change-levels", name = "Change on/off water levels", + description = "Change on/off water levels") + @Permission(scope = "watertank_user", permissions = {"/permission/admin/device-mgt/change-levels"}) + Response changeOnOffLevels(@PathParam("deviceId") String deviceId, + @QueryParam("on") int onLevel, + @QueryParam("off") int offLevel, + @Context HttpServletResponse response); + + /** + * Retrieve Sensor data for the given time period + * @param deviceId unique identifier for given device type instance + * @param sensorName name of the sensor + * @param from starting time + * @param to ending time + * @return response with List object which includes sensor data which is requested + */ + @Path("device/stats/{deviceId}/sensors/{sensorName}") + @GET + @Consumes("application/json") + @Produces("application/json") + Response getSensorStats(@PathParam("deviceId") String deviceId, @PathParam("sensorName") String sensorName, + @QueryParam("from") long from, @QueryParam("to") long to); + + @Path("/device/{device_id}") + @DELETE + @Permission(scope = "watertank_user", permissions = {"/permission/admin/device-mgt/removeDevice"}) + Response removeDevice(@PathParam("device_id") String deviceId); + + @Path("/device/{device_id}") + @PUT + @Permission(scope = "watertank_user", permissions = {"/permission/admin/device-mgt/updateDevice"}) + Response updateDevice(@PathParam("device_id") String deviceId, @QueryParam("name") String name); + + @Path("/device/{device_id}") + @GET + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Permission(scope = "watertank_user", permissions = {"/permission/admin/device-mgt/updateDevice"}) + Response getDevice(@PathParam("device_id") String deviceId); + + @Path("/devices") + @GET + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Permission(scope = "watertank_user", permissions = {"/permission/admin/device-mgt/devices"}) + Response getAllDevices(); + + @Path("/device/download") + @GET + @Produces("application/zip") + @Permission(scope = "watertank_user", permissions = {"/permission/admin/device-mgt/download"}) + Response downloadSketch(@QueryParam("deviceName") String deviceName, @QueryParam("sketchType") String sketchType); +} \ No newline at end of file diff --git a/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/DeviceTypeServiceImpl.java b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/DeviceTypeServiceImpl.java new file mode 100644 index 00000000..60364b50 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/DeviceTypeServiceImpl.java @@ -0,0 +1,434 @@ +/* +* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +* +* WSO2 Inc. licenses this file to you 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. +*/ + +package org.homeautomation.watertank.api; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.homeautomation.watertank.api.dto.DeviceJSON; +import org.homeautomation.watertank.api.dto.SensorRecord; +import org.homeautomation.watertank.api.util.APIUtil; +import org.homeautomation.watertank.api.util.ZipUtil; +import org.homeautomation.watertank.plugin.constants.DeviceTypeConstants; +import org.json.JSONObject; +import org.wso2.carbon.analytics.dataservice.commons.SORT; +import org.wso2.carbon.analytics.dataservice.commons.SortByField; +import org.wso2.carbon.analytics.datasource.commons.exception.AnalyticsException; +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.application.extension.APIManagementProviderService; +import org.wso2.carbon.apimgt.application.extension.dto.ApiApplicationKey; +import org.wso2.carbon.apimgt.application.extension.exception.APIManagerException; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.DeviceManagementException; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationException; +import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType; +import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature; +import org.wso2.carbon.device.mgt.iot.util.ZipArchive; +import org.wso2.carbon.identity.jwt.client.extension.JWTClient; +import org.wso2.carbon.identity.jwt.client.extension.dto.AccessTokenInfo; +import org.wso2.carbon.identity.jwt.client.extension.exception.JWTClientException; +import org.wso2.carbon.user.api.UserStoreException; + +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * This is the API which is used to control and manage device type functionality + */ +@SuppressWarnings("NonJaxWsWebServices") +@API(name = "watertank", version = "1.0.0", context = "/watertank", tags = "watertank") +@DeviceType(value = "watertank") +public class DeviceTypeServiceImpl implements DeviceTypeService { + + private static final String KEY_TYPE = "PRODUCTION"; + private static Log log = LogFactory.getLog(DeviceTypeService.class); + private static ApiApplicationKey apiApplicationKey; + private ConcurrentHashMap deviceToIpMap = new ConcurrentHashMap<>(); + + private static String shortUUID() { + UUID uuid = UUID.randomUUID(); + long l = ByteBuffer.wrap(uuid.toString().getBytes(StandardCharsets.UTF_8)).getLong(); + return Long.toString(l, Character.MAX_RADIX); + } + + /** + * @param agentInfo device owner,id + * @return true if device instance is added to map + */ + @Path("device/register") + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response registerDevice(final DeviceJSON agentInfo) { + String deviceId = agentInfo.deviceId; + if ((agentInfo.deviceId != null) && (agentInfo.owner != null)) { + deviceToIpMap.put(deviceId, agentInfo); + return Response.status(Response.Status.OK).build(); + } + return Response.status(Response.Status.NOT_ACCEPTABLE).build(); + } + + /** + /** + * @param deviceId unique identifier for given device type + * @param onLevel level to turn on the relay + * @param offLevel level to turn off thr relay + */ + @Path("device/{deviceId}/change-levels") + @POST + @Feature(code = "change-levels", name = "Change on/off water levels", + description = "Change on/off water levels") + public Response changeOnOffLevels(@PathParam("deviceId") String deviceId, + @QueryParam("on") int onLevel, + @QueryParam("off") int offLevel, + @Context HttpServletResponse response) { + try { + if (!APIUtil.getDeviceAccessAuthorizationService() + .isUserAuthorized(new DeviceIdentifier(deviceId, DeviceTypeConstants.DEVICE_TYPE))) { + return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); + } + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("on", onLevel); + jsonObject.put("off", offLevel); + + Map dynamicProperties = new HashMap<>(); + String publishTopic = APIUtil.getAuthenticatedUserTenantDomain() + + "/" + DeviceTypeConstants.DEVICE_TYPE + "/" + deviceId + "/command"; + dynamicProperties.put(DeviceTypeConstants.ADAPTER_TOPIC_PROPERTY, publishTopic); + APIUtil.getOutputEventAdapterService().publish(DeviceTypeConstants.MQTT_ADAPTER_NAME, + dynamicProperties, jsonObject.toString()); + return Response.ok().build(); + } catch (DeviceAccessAuthorizationException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + /** + * Retrieve Sensor data for the given time period + * + * @param deviceId unique identifier for given device type instance + * @param sensorName name of the sensor + * @param from starting time + * @param to ending time + * @return response with List object which includes sensor data which is requested + */ + @Path("device/stats/{deviceId}/sensors/{sensorName}") + @GET + @Consumes("application/json") + @Produces("application/json") + public Response getSensorStats(@PathParam("deviceId") String deviceId, @PathParam("sensorName") String sensorName, + @QueryParam("from") long from, @QueryParam("to") long to) { + String fromDate = String.valueOf(from); + String toDate = String.valueOf(to); + String query = "deviceId:" + deviceId + " AND deviceType:" + + DeviceTypeConstants.DEVICE_TYPE + " AND time : [" + fromDate + " TO " + toDate + "]"; + String sensorTableName; + switch (sensorName) { + case DeviceTypeConstants.STREAM_RELAY: + sensorTableName = DeviceTypeConstants.RELAY_EVENT_TABLE; + break; + case DeviceTypeConstants.STREAM_WATERLEVEL: + sensorTableName = DeviceTypeConstants.WATERLEVEL_EVENT_TABLE; + break; + default: + return Response.status(Response.Status.BAD_REQUEST).entity("Invalid event stream").build(); + } + + try { + if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized(new DeviceIdentifier(deviceId, + DeviceTypeConstants.DEVICE_TYPE))) { + return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); + } + List sortByFields = new ArrayList<>(); + SortByField sortByField = new SortByField("time", SORT.ASC, false); + sortByFields.add(sortByField); + List sensorRecords = APIUtil.getAllEventsForDevice(sensorTableName, query, sortByFields); + return Response.status(Response.Status.OK.getStatusCode()).entity(sensorRecords).build(); + } catch (AnalyticsException e) { + String errorMsg = "Error on retrieving stats on table " + sensorTableName + " with query " + query; + log.error(errorMsg); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).entity(errorMsg).build(); + } catch (DeviceAccessAuthorizationException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + /** + * Remove device type instance using device id + * + * @param deviceId unique identifier for given device type instance + */ + @Path("/device/{deviceId}") + @DELETE + public Response removeDevice(@PathParam("deviceId") String deviceId) { + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); + deviceIdentifier.setId(deviceId); + deviceIdentifier.setType(DeviceTypeConstants.DEVICE_TYPE); + if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized(deviceIdentifier)) { + return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); + } + boolean removed = APIUtil.getDeviceManagementService().disenrollDevice( + deviceIdentifier); + if (removed) { + return Response.ok().build(); + } else { + return Response.status(Response.Status.NOT_ACCEPTABLE.getStatusCode()).build(); + } + } catch (DeviceManagementException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } catch (DeviceAccessAuthorizationException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } + } + + /** + * Update device instance name + * + * @param deviceId unique identifier for given device type instance + * @param name new name for the device type instance + */ + @Path("/device/{deviceId}") + @PUT + public Response updateDevice(@PathParam("deviceId") String deviceId, @QueryParam("name") String name) { + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); + deviceIdentifier.setId(deviceId); + deviceIdentifier.setType(DeviceTypeConstants.DEVICE_TYPE); + if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized(deviceIdentifier)) { + return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); + } + Device device = APIUtil.getDeviceManagementService().getDevice(deviceIdentifier); + device.setDeviceIdentifier(deviceId); + device.getEnrolmentInfo().setDateOfLastUpdate(new Date().getTime()); + device.setName(name); + device.setType(DeviceTypeConstants.DEVICE_TYPE); + boolean updated = APIUtil.getDeviceManagementService().modifyEnrollment(device); + if (updated) { + return Response.ok().build(); + } else { + return Response.status(Response.Status.NOT_ACCEPTABLE.getStatusCode()).build(); + } + } catch (DeviceManagementException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } catch (DeviceAccessAuthorizationException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } + } + + /** + * To get device information + * + * @param deviceId unique identifier for given device type instance + * @return + */ + @Path("/device/{deviceId}") + @GET + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response getDevice(@PathParam("deviceId") String deviceId) { + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); + deviceIdentifier.setId(deviceId); + deviceIdentifier.setType(DeviceTypeConstants.DEVICE_TYPE); + if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized(deviceIdentifier)) { + return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); + } + Device device = APIUtil.getDeviceManagementService().getDevice(deviceIdentifier); + return Response.ok().entity(device).build(); + } catch (DeviceManagementException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } catch (DeviceAccessAuthorizationException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } + } + + /** + * Get all device type instance which belongs to user + * + * @return Array of devices which includes device's information + */ + @Path("/devices") + @GET + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response getAllDevices() { + try { + List userDevices = + APIUtil.getDeviceManagementService().getDevicesOfUser(APIUtil.getAuthenticatedUser()); + ArrayList userDevicesforwatertank = new ArrayList<>(); + for (Device device : userDevices) { + if (device.getType().equals(DeviceTypeConstants.DEVICE_TYPE) && + device.getEnrolmentInfo().getStatus().equals(EnrolmentInfo.Status.ACTIVE)) { + userDevicesforwatertank.add(device); + } + } + Device[] devices = userDevicesforwatertank.toArray(new Device[]{}); + return Response.ok().entity(devices).build(); + } catch (DeviceManagementException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } + } + + /** + * To download device type agent source code as zip file + * + * @param deviceName name for the device type instance + * @param sketchType folder name where device type agent was installed into server + * @return Agent source code as zip file + */ + @Path("/device/download") + @GET + @Produces("application/zip") + public Response downloadSketch(@QueryParam("deviceName") String deviceName, + @QueryParam("sketchType") String sketchType) { + try { + ZipArchive zipFile = createDownloadFile(APIUtil.getAuthenticatedUser(), deviceName, sketchType); + Response.ResponseBuilder response = Response.ok(FileUtils.readFileToByteArray(zipFile.getZipFile())); + response.status(Response.Status.OK); + response.type("application/zip"); + response.header("Content-Disposition", "attachment; filename=\"" + zipFile.getFileName() + "\""); + Response resp = response.build(); + zipFile.getZipFile().delete(); + return resp; + } catch (IllegalArgumentException ex) { + return Response.status(400).entity(ex.getMessage()).build();//bad request + } catch (DeviceManagementException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (JWTClientException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (APIManagerException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (IOException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (UserStoreException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } + } + + /** + * Register device into device management service + * + * @param deviceId unique identifier for given device type instance + * @param name name for the device type instance + * @return check whether device is installed into cdmf + */ + private boolean register(String deviceId, String name) { + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); + deviceIdentifier.setId(deviceId); + deviceIdentifier.setType(DeviceTypeConstants.DEVICE_TYPE); + if (APIUtil.getDeviceManagementService().isEnrolled(deviceIdentifier)) { + return false; + } + Device device = new Device(); + device.setDeviceIdentifier(deviceId); + EnrolmentInfo enrolmentInfo = new EnrolmentInfo(); + enrolmentInfo.setDateOfEnrolment(new Date().getTime()); + enrolmentInfo.setDateOfLastUpdate(new Date().getTime()); + enrolmentInfo.setStatus(EnrolmentInfo.Status.ACTIVE); + enrolmentInfo.setOwnership(EnrolmentInfo.OwnerShip.BYOD); + device.setName(name); + device.setType(DeviceTypeConstants.DEVICE_TYPE); + enrolmentInfo.setOwner(APIUtil.getAuthenticatedUser()); + device.setEnrolmentInfo(enrolmentInfo); + boolean added = APIUtil.getDeviceManagementService().enrollDevice(device); + if (added) { + APIUtil.registerApiAccessRoles(APIUtil.getAuthenticatedUser()); + } + return added; + } catch (DeviceManagementException e) { + log.error(e.getMessage(), e); + return false; + } + } + + private ZipArchive createDownloadFile(String owner, String deviceName, String sketchType) + throws DeviceManagementException, JWTClientException, APIManagerException, + UserStoreException { + //create new device id + String deviceId = shortUUID(); + if (apiApplicationKey == null) { + String applicationUsername = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm() + .getRealmConfiguration().getAdminUserName(); + applicationUsername = applicationUsername + "@" + APIUtil.getAuthenticatedUserTenantDomain(); + APIManagementProviderService apiManagementProviderService = APIUtil.getAPIManagementProviderService(); + String[] tags = {DeviceTypeConstants.DEVICE_TYPE}; + apiApplicationKey = apiManagementProviderService.generateAndRetrieveApplicationKeys( + DeviceTypeConstants.DEVICE_TYPE, tags, KEY_TYPE, applicationUsername, true); + } + JWTClient jwtClient = APIUtil.getJWTClientManagerService().getJWTClient(); + String scopes = "device_type_" + DeviceTypeConstants.DEVICE_TYPE + " device_" + deviceId; + AccessTokenInfo accessTokenInfo = jwtClient.getAccessToken(apiApplicationKey.getConsumerKey(), + apiApplicationKey.getConsumerSecret(), owner + "@" + APIUtil.getAuthenticatedUserTenantDomain(), scopes); + + //create token + String accessToken = accessTokenInfo.getAccessToken(); + String refreshToken = accessTokenInfo.getRefreshToken(); + boolean status = register(deviceId, deviceName); + if (!status) { + String msg = "Error occurred while registering the device with " + "id: " + deviceId + " owner:" + owner; + throw new DeviceManagementException(msg); + } + ZipUtil ziputil = new ZipUtil(); + ZipArchive zipFile = ziputil.createZipFile(owner, APIUtil.getTenantDomainOftheUser(), sketchType, + deviceId, deviceName, accessToken, refreshToken); + return zipFile; + } +} + diff --git a/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/dto/DeviceJSON.java b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/dto/DeviceJSON.java new file mode 100644 index 00000000..74395100 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/dto/DeviceJSON.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.api.dto; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * These information are sent by agent in each request to server + */ +@XmlRootElement +@JsonIgnoreProperties(ignoreUnknown = true) +public class DeviceJSON { + @XmlElement(required = true) + public String owner; + @XmlElement(required = true) + public String deviceId; + @XmlElement(required = true) + public Float sensorValue; +} diff --git a/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/dto/SensorRecord.java b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/dto/SensorRecord.java new file mode 100644 index 00000000..c4aa657d --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/dto/SensorRecord.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.api.dto; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * This stores sensor event data for watertank. + */ +@XmlRootElement +@JsonIgnoreProperties(ignoreUnknown = true) +public class SensorRecord { + + @XmlElementWrapper(required = true, name = "values") + private Map values; + + /** + * Unique identifier for each recode + */ + @XmlElement(required = false, name = "id") + private String id; + + /** + * Gets the values. + * @return the values + */ + public Map getValues() { + return values; + } + + /** + * Sets the values. + * @param values + */ + public void setValues(Map values) { + this.values = values; + } + + /** + * Gets the id. + * @return the id + */ + public String getId() { + return id; + } + + /** + * Sets the id. + * @param id set unique identifier + */ + public void setId(String id) { + this.id = id; + } + + @Override + public String toString() { + List valueList = new ArrayList(); + for (Map.Entry entry : values.entrySet()) { + valueList.add(entry.getKey() + ":" + entry.getValue()); + } + return valueList.toString(); + + } + +} diff --git a/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/exception/DeviceTypeException.java b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/exception/DeviceTypeException.java new file mode 100644 index 00000000..c4d4f3d8 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/exception/DeviceTypeException.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.api.exception; + +public class DeviceTypeException extends Exception { + + private static final long serialVersionUID = 2736466563251105441L; + + private String errorMessage; + + public DeviceTypeException(String msg, DeviceTypeException nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public DeviceTypeException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public DeviceTypeException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public DeviceTypeException() { + super(); + } + + public DeviceTypeException(Throwable cause) { + super(cause); + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } +} diff --git a/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/APIUtil.java b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/APIUtil.java new file mode 100644 index 00000000..bff9bc41 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/APIUtil.java @@ -0,0 +1,221 @@ +package org.homeautomation.watertank.api.util; + +import org.homeautomation.watertank.api.dto.SensorRecord; + +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService; +import org.wso2.carbon.analytics.api.AnalyticsDataAPI; +import org.wso2.carbon.analytics.dataservice.core.AnalyticsDataServiceUtils; +import org.wso2.carbon.analytics.dataservice.commons.AnalyticsDataResponse; +import org.wso2.carbon.analytics.dataservice.commons.SearchResultEntry; +import org.wso2.carbon.analytics.dataservice.commons.SortByField; +import org.wso2.carbon.analytics.datasource.commons.Record; +import org.wso2.carbon.analytics.datasource.commons.exception.AnalyticsException; +import org.wso2.carbon.apimgt.application.extension.APIManagementProviderService; +import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService; +import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; +import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.api.UserStoreManager; +import org.wso2.carbon.user.core.service.RealmService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class provides utility functions used by REST-API. + */ +public class APIUtil { + + private static Log log = LogFactory.getLog(APIUtil.class); + + public static String getAuthenticatedUser() { + PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + String username = threadLocalCarbonContext.getUsername(); + String tenantDomain = threadLocalCarbonContext.getTenantDomain(); + if (username.endsWith(tenantDomain)) { + return username.substring(0, username.lastIndexOf("@")); + } + return username; + } + + public static DeviceManagementProviderService getDeviceManagementService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + DeviceManagementProviderService deviceManagementProviderService = + (DeviceManagementProviderService) ctx.getOSGiService(DeviceManagementProviderService.class, null); + if (deviceManagementProviderService == null) { + String msg = "Device Management service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return deviceManagementProviderService; + } + + public static APIManagementProviderService getAPIManagementProviderService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + APIManagementProviderService apiManagementProviderService = + (APIManagementProviderService) ctx.getOSGiService(APIManagementProviderService.class, null); + if (apiManagementProviderService == null) { + String msg = "API management provider service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return apiManagementProviderService; + } + + public static JWTClientManagerService getJWTClientManagerService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + JWTClientManagerService jwtClientManagerService = + (JWTClientManagerService) ctx.getOSGiService(JWTClientManagerService.class, null); + if (jwtClientManagerService == null) { + String msg = "JWT Client manager service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return jwtClientManagerService; + } + + public static String getTenantDomainOftheUser() { + PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + return threadLocalCarbonContext.getTenantDomain(); + } + + public static UserStoreManager getUserStoreManager() { + RealmService realmService; + UserStoreManager userStoreManager; + try { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + realmService = (RealmService) ctx.getOSGiService(RealmService.class, null); + if (realmService == null) { + String msg = "Realm service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + int tenantId = ctx.getTenantId(); + userStoreManager = realmService.getTenantUserRealm(tenantId).getUserStoreManager(); + } catch (UserStoreException e) { + String msg = "Error occurred while retrieving current user store manager"; + log.error(msg, e); + throw new IllegalStateException(msg); + } + return userStoreManager; + } + + public static void registerApiAccessRoles(String user) { + UserStoreManager userStoreManager = null; + try { + userStoreManager = getUserStoreManager(); + String[] userList = new String[]{user}; + if (userStoreManager != null) { + String rolesOfUser[] = userStoreManager.getRoleListOfUser(user); + if (!userStoreManager.isExistingRole(Constants.DEFAULT_ROLE_NAME)) { + userStoreManager.addRole(Constants.DEFAULT_ROLE_NAME, userList, Constants.DEFAULT_PERMISSION); + } else if (rolesOfUser != null && Arrays.asList(rolesOfUser).contains(Constants.DEFAULT_ROLE_NAME)) { + return; + } else { + userStoreManager.updateUserListOfRole(Constants.DEFAULT_ROLE_NAME, new String[0], userList); + } + } + } catch (UserStoreException e) { + log.error("Error while creating a role and adding a user for watertank.", e); + } + } + + public static DeviceAccessAuthorizationService getDeviceAccessAuthorizationService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + DeviceAccessAuthorizationService deviceAccessAuthorizationService = + (DeviceAccessAuthorizationService) ctx.getOSGiService(DeviceAccessAuthorizationService.class, null); + if (deviceAccessAuthorizationService == null) { + String msg = "Device Authorization service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return deviceAccessAuthorizationService; + } + + public static List getAllEventsForDevice(String tableName, String query, + List sortByFields) throws AnalyticsException { + int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); + AnalyticsDataAPI analyticsDataAPI = getAnalyticsDataAPI(); + int eventCount = analyticsDataAPI.searchCount(tenantId, tableName, query); + if (eventCount == 0) { + return null; + } + List resultEntries = analyticsDataAPI.search(tenantId, tableName, query, 0, eventCount, + sortByFields); + List recordIds = getRecordIds(resultEntries); + AnalyticsDataResponse response = analyticsDataAPI.get(tenantId, tableName, 1, null, recordIds); + Map sensorDatas = createSensorData(AnalyticsDataServiceUtils.listRecords( + analyticsDataAPI, response)); + List sortedSensorData = getSortedSensorData(sensorDatas, resultEntries); + return sortedSensorData; + } + + public static List getSortedSensorData(Map sensorDatas, + List searchResults) { + List sortedRecords = new ArrayList<>(); + for (SearchResultEntry searchResultEntry : searchResults) { + sortedRecords.add(sensorDatas.get(searchResultEntry.getId())); + } + return sortedRecords; + } + + private static List getRecordIds(List searchResults) { + List ids = new ArrayList<>(); + for (SearchResultEntry searchResult : searchResults) { + ids.add(searchResult.getId()); + } + return ids; + } + + public static Map createSensorData(List records) { + Map sensorDatas = new HashMap<>(); + for (Record record : records) { + SensorRecord sensorData = createSensorData(record); + sensorDatas.put(sensorData.getId(), sensorData); + } + return sensorDatas; + } + + public static SensorRecord createSensorData(Record record) { + SensorRecord recordBean = new SensorRecord(); + recordBean.setId(record.getId()); + recordBean.setValues(record.getValues()); + return recordBean; + } + + public static AnalyticsDataAPI getAnalyticsDataAPI() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + AnalyticsDataAPI analyticsDataAPI = + (AnalyticsDataAPI) ctx.getOSGiService(AnalyticsDataAPI.class, null); + if (analyticsDataAPI == null) { + String msg = "Analytics api service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return analyticsDataAPI; + } + + public static String getAuthenticatedUserTenantDomain() { + PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + return threadLocalCarbonContext.getTenantDomain(); + } + + public static OutputEventAdapterService getOutputEventAdapterService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + OutputEventAdapterService outputEventAdapterService = + (OutputEventAdapterService) ctx.getOSGiService(OutputEventAdapterService.class, null); + if (outputEventAdapterService == null) { + String msg = "Device Authorization service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return outputEventAdapterService; + } +} diff --git a/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/Constants.java b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/Constants.java new file mode 100644 index 00000000..9b91d5f2 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/Constants.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.api.util; + +import org.wso2.carbon.user.core.Permission; + +/** + * This hold the constants related to the device type. + */ +public class Constants { + + public static final String DEFAULT_PERMISSION_RESOURCE = "/permission/admin/device-mgt/watertank/user"; + public static final String DEFAULT_ROLE_NAME = "watertank_user"; + public static final Permission DEFAULT_PERMISSION[] + = new Permission[]{new Permission(Constants.DEFAULT_PERMISSION_RESOURCE, "ui.execute")}; +} diff --git a/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/ZipUtil.java b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/ZipUtil.java new file mode 100644 index 00000000..21bb3c40 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/java/org/homeautomation/watertank/api/util/ZipUtil.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.api.util; + +import org.homeautomation.watertank.plugin.mqtt.MqttConfig; + +import org.wso2.carbon.device.mgt.common.DeviceManagementException; +import org.wso2.carbon.device.mgt.iot.util.Utils; +import org.wso2.carbon.device.mgt.iot.util.ZipArchive; +import org.wso2.carbon.utils.CarbonUtils; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * This is used to create a zip file that includes the necessary configuration required for the agent. + */ +public class ZipUtil { + + private static final String HTTPS_PORT_PROPERTY = "httpsPort"; + private static final String HTTP_PORT_PROPERTY = "httpPort"; + + private static final String LOCALHOST = "localhost"; + private static final String HTTPS_PROTOCOL_APPENDER = "https://"; + private static final String HTTP_PROTOCOL_APPENDER = "http://"; + + public ZipArchive createZipFile(String owner, String tenantDomain, String deviceType, + String deviceId, String deviceName, String token, + String refreshToken) throws DeviceManagementException { + + String sketchFolder = "repository" + File.separator + "resources" + File.separator + "sketches"; + String archivesPath = CarbonUtils.getCarbonHome() + File.separator + sketchFolder + File.separator + "archives" + + File.separator + deviceId; + String templateSketchPath = sketchFolder + File.separator + deviceType; + String iotServerIP; + + try { + iotServerIP = Utils.getServerUrl(); + String httpsServerPort = System.getProperty(HTTPS_PORT_PROPERTY); + String httpServerPort = System.getProperty(HTTP_PORT_PROPERTY); + String httpsServerEP = HTTPS_PROTOCOL_APPENDER + iotServerIP + ":" + httpsServerPort; + String httpServerEP = HTTP_PROTOCOL_APPENDER + iotServerIP + ":" + httpServerPort; + String apimEndpoint = httpsServerEP; + String mqttEndpoint = MqttConfig.getInstance().getBrokerEndpoint(); + if (mqttEndpoint.contains(LOCALHOST)) { + mqttEndpoint = mqttEndpoint.replace(LOCALHOST, iotServerIP); + } + + String[] mqttEPParams = mqttEndpoint.split(":"); + String mqttEPIP = mqttEPParams[1].replace("//", ""); + String mqttPort = mqttEPParams[2]; + + Map contextParams = new HashMap<>(); + contextParams.put("SERVER_NAME", APIUtil.getTenantDomainOftheUser()); + contextParams.put("DEVICE_OWNER", owner); + contextParams.put("DEVICE_ID", deviceId); + contextParams.put("DEVICE_NAME", deviceName); + contextParams.put("HTTPS_EP", httpsServerEP); + contextParams.put("HTTP_EP", httpServerEP); + contextParams.put("APIM_EP", apimEndpoint); + contextParams.put("MQTT_EP", mqttEPIP); + contextParams.put("MQTT_PORT", mqttPort); + contextParams.put("DEVICE_TOKEN", token); + contextParams.put("DEVICE_REFRESH_TOKEN", refreshToken); + + ZipArchive zipFile; + zipFile = Utils.getSketchArchive(archivesPath, templateSketchPath, contextParams, deviceName); + return zipFile; + } catch (IOException e) { + throw new DeviceManagementException("Zip File Creation Failed", e); + } + } +} diff --git a/modules/samples/watertank/component/api/src/main/webapp/META-INF/permissions.xml b/modules/samples/watertank/component/api/src/main/webapp/META-INF/permissions.xml new file mode 100644 index 00000000..a1d3123f --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/webapp/META-INF/permissions.xml @@ -0,0 +1,87 @@ + + + + + + + + + + Get device + /device-mgt/watertank/user + /device/* + GET + watertank_user + + + Remove device + /device-mgt/watertank/user + /device/* + DELETE + watertank_user + + + Download device + /device-mgt/watertank/user + /device/download + GET + watertank_user + + + Update device + /device-mgt/watertank/user + /device/* + POST + watertank_user + + + Get Devices + /device-mgt/watertank/user + /device + GET + watertank_user + + + Register Device + /device-mgt/watertank/user + /device/register + POST + watertank_device + + + Control Sensor + /device-mgt/watertank/user + /device/*/change-levels + POST + watertank_user + + + Get Stats + /device-mgt/watertank/user + /device/stats/* + GET + watertank_device + + \ No newline at end of file diff --git a/modules/samples/watertank/component/api/src/main/webapp/META-INF/webapp-classloading.xml b/modules/samples/watertank/component/api/src/main/webapp/META-INF/webapp-classloading.xml new file mode 100644 index 00000000..fa446191 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/webapp/META-INF/webapp-classloading.xml @@ -0,0 +1,33 @@ + + + + + + + + + false + + + CXF,Carbon + diff --git a/modules/samples/watertank/component/api/src/main/webapp/WEB-INF/cxf-servlet.xml b/modules/samples/watertank/component/api/src/main/webapp/WEB-INF/cxf-servlet.xml new file mode 100644 index 00000000..5a222d12 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/webapp/WEB-INF/cxf-servlet.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/api/src/main/webapp/WEB-INF/web.xml b/modules/samples/watertank/component/api/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..612804f2 --- /dev/null +++ b/modules/samples/watertank/component/api/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,46 @@ + + + WSO2 IoT Server + WSO2 IoT Server + + + CXFServlet + org.apache.cxf.transport.servlet.CXFServlet + 1 + + + CXFServlet + /* + + + isAdminService + false + + + doAuthentication + true + + + isSharedWithAllTenants + false + + + providerTenantDomain + carbon.super + + + + + managed-api-enabled + true + + + managed-api-owner + admin + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/plugin/pom.xml b/modules/samples/watertank/component/plugin/pom.xml new file mode 100644 index 00000000..0f2d1a08 --- /dev/null +++ b/modules/samples/watertank/component/plugin/pom.xml @@ -0,0 +1,133 @@ + + + + + + org.homeautomation + watertank-component + 1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + 1.0-SNAPSHOT + ${project-base-package}.plugin + bundle + ${project-base-package}.plugin + http://wso2.org + + + + org.apache.felix + maven-scr-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + UTF-8 + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.apache.felix + maven-bundle-plugin + ${org.apache.felix.version} + true + + + ${project-base-package}.plugin + ${project-base-package}.plugin + 1.0-SNAPSHOT + IoT Server Impl Bundle + ${project-base-package}.plugin.internal + + org.osgi.framework, + org.osgi.service.component, + org.apache.commons.logging, + javax.naming;resolution:=optional, + javax.sql;resolution:=optional, + org.wso2.carbon.device.mgt.common.*, + org.wso2.carbon.device.mgt.common, + org.wso2.carbon.device.mgt.iot.*, + org.wso2.carbon.device.mgt.extensions.feature.mgt.*, + org.wso2.carbon.utils.*, + org.wso2.carbon.event.output.adapter.core, + org.wso2.carbon.event.output.adapter.core.exception, + org.wso2.carbon.base, + org.wso2.carbon.core.util, + org.wso2.carbon.context, + org.wso2.carbon.core, + org.apache.commons.codec.binary + + + !${project-base-package}.plugin.internal, + ${project-base-package}.plugin.* + + + + + + + + + commons-codec.wso2 + commons-codec + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + org.wso2.carbon + org.wso2.carbon.logging + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + + + org.wso2.carbon + org.wso2.carbon.ndatasource.core + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.extensions + + + org.wso2.carbon + org.wso2.carbon.utils + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.output.adapter.core + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/constants/DeviceTypeConstants.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/constants/DeviceTypeConstants.java new file mode 100644 index 00000000..79ca70e0 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/constants/DeviceTypeConstants.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.constants; + +import org.wso2.carbon.utils.CarbonUtils; +import java.io.File; + +public class DeviceTypeConstants { + public final static String DEVICE_TYPE = "watertank"; + public final static String DEVICE_PLUGIN_DEVICE_NAME = "DEVICE_NAME"; + public final static String DEVICE_PLUGIN_DEVICE_ID = "watertank_DEVICE_ID"; + public final static String STATE_ON = "ON"; + public final static String STATE_OFF = "OFF"; + + //sensor events summarized table name + public final static String STREAM_RELAY = "relay"; + public final static String STREAM_WATERLEVEL = "waterlevel"; + public final static String RELAY_EVENT_TABLE = "DEVICE_relay_SUMMARY"; + public final static String WATERLEVEL_EVENT_TABLE = "DEVICE_waterlevel_SUMMARY"; + + public static final String DATA_SOURCE_NAME = "jdbc/watertankDM_DB"; + public final static String DEVICE_TYPE_PROVIDER_DOMAIN = "carbon.super"; + + //mqtt transport related constants + public static final String MQTT_ADAPTER_NAME = "watertank_mqtt"; + public static final String MQTT_ADAPTER_TYPE = "oauth-mqtt"; + public static final String ADAPTER_TOPIC_PROPERTY = "topic"; + public static final String MQTT_PORT = "\\{mqtt.broker.port\\}"; + public static final String MQTT_BROKER_HOST = "\\{mqtt.broker.host\\}"; + public static final String CARBON_CONFIG_PORT_OFFSET = "Ports.Offset"; + public static final String DEFAULT_CARBON_LOCAL_IP_PROPERTY = "carbon.local.ip"; + public static final int CARBON_DEFAULT_PORT_OFFSET = 0; + public static final int DEFAULT_MQTT_PORT = 1883; + public static final String RESOURCE = "resource"; + + public static final String USERNAME_PROPERTY_KEY = "username"; + public static final String DCR_PROPERTY_KEY = "dcrUrl"; + public static final String BROKER_URL_PROPERTY_KEY = "url"; + public static final String SCOPES_PROPERTY_KEY = "scopes"; + public static final String QOS_PROPERTY_KEY = "qos"; + public static final String CLIENT_ID_PROPERTY_KEY = "qos"; + public static final String CLEAR_SESSION_PROPERTY_KEY = "clearSession"; + + public static final String MQTT_CONFIG_LOCATION = CarbonUtils.getEtcCarbonConfigDirPath() + File.separator + + "mqtt.properties"; +} + diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/exception/DeviceMgtPluginException.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/exception/DeviceMgtPluginException.java new file mode 100644 index 00000000..5f5283c7 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/exception/DeviceMgtPluginException.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.exception; + +public class DeviceMgtPluginException extends Exception { + + private String errorMessage; + + public DeviceMgtPluginException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public DeviceMgtPluginException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public DeviceMgtPluginException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public DeviceMgtPluginException() { + super(); + } + + public DeviceMgtPluginException(Throwable cause) { + super(cause); + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + +} \ No newline at end of file diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/DeviceTypeManager.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/DeviceTypeManager.java new file mode 100644 index 00000000..4423afb3 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/DeviceTypeManager.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.homeautomation.watertank.plugin.impl.dao.DeviceTypeDAO; +import org.homeautomation.watertank.plugin.exception.DeviceMgtPluginException; +import org.homeautomation.watertank.plugin.impl.feature.DeviceTypeFeatureManager; +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.DeviceManagementException; +import org.wso2.carbon.device.mgt.common.DeviceManager; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.FeatureManager; +import org.wso2.carbon.device.mgt.common.configuration.mgt.TenantConfiguration; +import org.wso2.carbon.device.mgt.common.license.mgt.License; +import org.wso2.carbon.device.mgt.common.license.mgt.LicenseManagementException; + +import java.util.List; + + +/** + * This represents the watertank implementation of DeviceManagerService. + */ +public class DeviceTypeManager implements DeviceManager { + + private static final Log log = LogFactory.getLog(DeviceTypeManager.class); + private static final DeviceTypeDAO deviceTypeDAO = new DeviceTypeDAO(); + private FeatureManager featureManager = new DeviceTypeFeatureManager(); + + @Override + public FeatureManager getFeatureManager() { + return featureManager; + } + + @Override + public boolean saveConfiguration(TenantConfiguration tenantConfiguration) + throws DeviceManagementException { + //TODO implement this + return false; + } + + @Override + public TenantConfiguration getConfiguration() throws DeviceManagementException { + //TODO implement this + return null; + } + + @Override + public boolean enrollDevice(Device device) throws DeviceManagementException { + boolean status; + try { + if (log.isDebugEnabled()) { + log.debug("Enrolling a new watertank device : " + device.getDeviceIdentifier()); + } + DeviceTypeDAO.beginTransaction(); + status = deviceTypeDAO.getDeviceTypeDAO().addDevice(device); + DeviceTypeDAO.commitTransaction(); + } catch (DeviceMgtPluginException e) { + try { + DeviceTypeDAO.rollbackTransaction(); + } catch (DeviceMgtPluginException iotDAOEx) { + log.warn("Error occurred while roll back the device enrol transaction :" + device.toString(), iotDAOEx); + } + String msg = "Error while enrolling the watertank device : " + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return status; + } + + @Override + public boolean modifyEnrollment(Device device) throws DeviceManagementException { + boolean status; + try { + if (log.isDebugEnabled()) { + log.debug("Modifying the watertank device enrollment data"); + } + DeviceTypeDAO.beginTransaction(); + status = deviceTypeDAO.getDeviceTypeDAO().updateDevice(device); + DeviceTypeDAO.commitTransaction(); + } catch (DeviceMgtPluginException e) { + try { + DeviceTypeDAO.rollbackTransaction(); + } catch (DeviceMgtPluginException iotDAOEx) { + String msg = "Error occurred while roll back the update device transaction :" + device.toString(); + log.warn(msg, iotDAOEx); + } + String msg = "Error while updating the enrollment of the watertank device : " + + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return status; + } + + @Override + public boolean disenrollDevice(DeviceIdentifier deviceId) throws DeviceManagementException { + boolean status; + try { + if (log.isDebugEnabled()) { + log.debug("Dis-enrolling watertank device : " + deviceId); + } + DeviceTypeDAO.beginTransaction(); + status = deviceTypeDAO.getDeviceTypeDAO().deleteDevice(deviceId.getId()); + DeviceTypeDAO.commitTransaction(); + } catch (DeviceMgtPluginException e) { + try { + DeviceTypeDAO.rollbackTransaction(); + } catch (DeviceMgtPluginException iotDAOEx) { + String msg = "Error occurred while roll back the device dis enrol transaction :" + deviceId.toString(); + log.warn(msg, iotDAOEx); + } + String msg = "Error while removing the watertank device : " + deviceId.getId(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return status; + } + + @Override + public boolean isEnrolled(DeviceIdentifier deviceId) throws DeviceManagementException { + boolean isEnrolled = false; + try { + if (log.isDebugEnabled()) { + log.debug("Checking the enrollment of watertank device : " + deviceId.getId()); + } + Device iotDevice = + deviceTypeDAO.getDeviceTypeDAO().getDevice(deviceId.getId()); + if (iotDevice != null) { + isEnrolled = true; + } + } catch (DeviceMgtPluginException e) { + String msg = "Error while checking the enrollment status of watertank device : " + + deviceId.getId(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return isEnrolled; + } + + @Override + public boolean isActive(DeviceIdentifier deviceId) throws DeviceManagementException { + return true; + } + + @Override + public boolean setActive(DeviceIdentifier deviceId, boolean status) + throws DeviceManagementException { + return true; + } + + @Override + public Device getDevice(DeviceIdentifier deviceId) throws DeviceManagementException { + Device device; + try { + if (log.isDebugEnabled()) { + log.debug("Getting the details of watertank device : " + deviceId.getId()); + } + device = deviceTypeDAO.getDeviceTypeDAO().getDevice(deviceId.getId()); + } catch (DeviceMgtPluginException e) { + String msg = "Error while fetching the watertank device : " + deviceId.getId(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return device; + } + + @Override + public boolean setOwnership(DeviceIdentifier deviceId, String ownershipType) + throws DeviceManagementException { + return true; + } + + public boolean isClaimable(DeviceIdentifier deviceIdentifier) throws DeviceManagementException { + return false; + } + + @Override + public boolean setStatus(DeviceIdentifier deviceId, String currentOwner, + EnrolmentInfo.Status status) throws DeviceManagementException { + return false; + } + + @Override + public License getLicense(String s) throws LicenseManagementException { + return null; + } + + @Override + public void addLicense(License license) throws LicenseManagementException { + + } + + @Override + public boolean requireDeviceAuthorization() { + return true; + } + + @Override + public boolean updateDeviceInfo(DeviceIdentifier deviceIdentifier, Device device) throws DeviceManagementException { + boolean status; + try { + if (log.isDebugEnabled()) { + log.debug("updating the details of watertank device : " + deviceIdentifier); + } + DeviceTypeDAO.beginTransaction(); + status = deviceTypeDAO.getDeviceTypeDAO().updateDevice(device); + DeviceTypeDAO.commitTransaction(); + } catch (DeviceMgtPluginException e) { + try { + DeviceTypeDAO.rollbackTransaction(); + } catch (DeviceMgtPluginException iotDAOEx) { + String msg = "Error occurred while roll back the update device info transaction :" + device.toString(); + log.warn(msg, iotDAOEx); + } + String msg = + "Error while updating the watertank device : " + deviceIdentifier; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return status; + } + + @Override + public List getAllDevices() throws DeviceManagementException { + List devices; + try { + if (log.isDebugEnabled()) { + log.debug("Fetching the details of all watertank devices"); + } + devices = deviceTypeDAO.getDeviceTypeDAO().getAllDevices(); + } catch (DeviceMgtPluginException e) { + String msg = "Error while fetching all watertank devices."; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return devices; + } +} \ No newline at end of file diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/DeviceTypeManagerService.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/DeviceTypeManagerService.java new file mode 100644 index 00000000..4f4fb04f --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/DeviceTypeManagerService.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.impl; + +import org.homeautomation.watertank.plugin.constants.DeviceTypeConstants; +import org.wso2.carbon.device.mgt.common.ProvisioningConfig; +import org.wso2.carbon.device.mgt.common.DeviceManagementException; +import org.wso2.carbon.device.mgt.common.DeviceManager; +import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager; +import org.wso2.carbon.device.mgt.common.push.notification.PushNotificationConfig; +import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; + +public class DeviceTypeManagerService implements DeviceManagementService { + private DeviceManager deviceManager; + + @Override + public String getType() { + return DeviceTypeConstants.DEVICE_TYPE; + } + + @Override + public void init() throws DeviceManagementException { + this.deviceManager = new DeviceTypeManager(); + } + + @Override + public DeviceManager getDeviceManager() { + return deviceManager; + } + + @Override + public ApplicationManager getApplicationManager() { + return null; + } + + @Override + public ProvisioningConfig getProvisioningConfig() { + return new ProvisioningConfig(DeviceTypeConstants.DEVICE_TYPE_PROVIDER_DOMAIN, false); + } + + @Override + public PushNotificationConfig getPushNotificationConfig() { + return null; + } + +} diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/dao/DeviceTypeDAO.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/dao/DeviceTypeDAO.java new file mode 100644 index 00000000..bfb32394 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/dao/DeviceTypeDAO.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.impl.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.homeautomation.watertank.plugin.constants.DeviceTypeConstants; +import org.homeautomation.watertank.plugin.impl.dao.impl.DeviceTypeDAOImpl; +import org.homeautomation.watertank.plugin.exception.DeviceMgtPluginException; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public class DeviceTypeDAO { + + private static final Log log = LogFactory.getLog(DeviceTypeDAO.class); + static DataSource dataSource; // package local variable + private static ThreadLocal currentConnection = new ThreadLocal<>(); + + public DeviceTypeDAO() { + initDeviceTypeDAO(); + } + + public static void initDeviceTypeDAO() { + try { + Context ctx = new InitialContext(); + dataSource = (DataSource) ctx.lookup(DeviceTypeConstants.DATA_SOURCE_NAME); + } catch (NamingException e) { + log.error("Error while looking up the data source: " + + DeviceTypeConstants.DATA_SOURCE_NAME); + } + } + + public static void beginTransaction() throws DeviceMgtPluginException { + try { + Connection conn = dataSource.getConnection(); + conn.setAutoCommit(false); + currentConnection.set(conn); + } catch (SQLException e) { + throw new DeviceMgtPluginException("Error occurred while retrieving datasource connection", e); + } + } + + public static Connection getConnection() throws DeviceMgtPluginException { + if (currentConnection.get() == null) { + try { + currentConnection.set(dataSource.getConnection()); + } catch (SQLException e) { + throw new DeviceMgtPluginException("Error occurred while retrieving data source connection", + e); + } + } + return currentConnection.get(); + } + + public static void commitTransaction() throws DeviceMgtPluginException { + try { + Connection conn = currentConnection.get(); + if (conn != null) { + conn.commit(); + } else { + if (log.isDebugEnabled()) { + log.debug("Datasource connection associated with the current thread is null, hence commit " + + "has not been attempted"); + } + } + } catch (SQLException e) { + throw new DeviceMgtPluginException("Error occurred while committing the transaction", e); + } finally { + closeConnection(); + } + } + + public static void closeConnection() throws DeviceMgtPluginException { + + Connection con = currentConnection.get(); + if (con != null) { + try { + con.close(); + } catch (SQLException e) { + log.error("Error occurred while close the connection"); + } + } + currentConnection.remove(); + } + + public static void rollbackTransaction() throws DeviceMgtPluginException { + try { + Connection conn = currentConnection.get(); + if (conn != null) { + conn.rollback(); + } else { + if (log.isDebugEnabled()) { + log.debug("Datasource connection associated with the current thread is null, hence rollback " + + "has not been attempted"); + } + } + } catch (SQLException e) { + throw new DeviceMgtPluginException("Error occurred while rollback the transaction", e); + } finally { + closeConnection(); + } + } + + public DeviceTypeDAOImpl getDeviceTypeDAO() { + return new DeviceTypeDAOImpl(); + } +} \ No newline at end of file diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/dao/impl/DeviceTypeDAOImpl.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/dao/impl/DeviceTypeDAOImpl.java new file mode 100644 index 00000000..24e7e3c6 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/dao/impl/DeviceTypeDAOImpl.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.impl.dao.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.homeautomation.watertank.plugin.constants.DeviceTypeConstants; +import org.homeautomation.watertank.plugin.exception.DeviceMgtPluginException; +import org.homeautomation.watertank.plugin.impl.dao.DeviceTypeDAO; +import org.homeautomation.watertank.plugin.impl.util.DeviceTypeUtils; + +import org.wso2.carbon.device.mgt.common.Device; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Implements IotDeviceDAO for watertank Devices. + */ +public class DeviceTypeDAOImpl { + + + private static final Log log = LogFactory.getLog(DeviceTypeDAOImpl.class); + + public Device getDevice(String deviceId) throws DeviceMgtPluginException { + Connection conn = null; + PreparedStatement stmt = null; + Device iotDevice = null; + ResultSet resultSet = null; + try { + conn = DeviceTypeDAO.getConnection(); + String selectDBQuery = + "SELECT watertank_DEVICE_ID, DEVICE_NAME" + + " FROM watertank_DEVICE WHERE watertank_DEVICE_ID = ?"; + stmt = conn.prepareStatement(selectDBQuery); + stmt.setString(1, deviceId); + resultSet = stmt.executeQuery(); + + if (resultSet.next()) { + iotDevice = new Device(); + iotDevice.setName(resultSet.getString( + DeviceTypeConstants.DEVICE_PLUGIN_DEVICE_NAME)); + if (log.isDebugEnabled()) { + log.debug("watertank device " + deviceId + " data has been fetched from " + + "watertank database."); + } + } + } catch (SQLException e) { + String msg = "Error occurred while fetching watertank device : '" + deviceId + "'"; + log.error(msg, e); + throw new DeviceMgtPluginException(msg, e); + } finally { + DeviceTypeUtils.cleanupResources(stmt, resultSet); + DeviceTypeDAO.closeConnection(); + } + return iotDevice; + } + + public boolean addDevice(Device device) throws DeviceMgtPluginException { + boolean status = false; + Connection conn; + PreparedStatement stmt = null; + try { + conn = DeviceTypeDAO.getConnection(); + String createDBQuery = + "INSERT INTO watertank_DEVICE(watertank_DEVICE_ID, DEVICE_NAME) VALUES (?, ?)"; + stmt = conn.prepareStatement(createDBQuery); + stmt.setString(1, device.getDeviceIdentifier()); + stmt.setString(2, device.getName()); + int rows = stmt.executeUpdate(); + if (rows > 0) { + status = true; + if (log.isDebugEnabled()) { + log.debug("watertank device " + device.getDeviceIdentifier() + " data has been" + + " added to the watertank database."); + } + } + } catch (SQLException e) { + String msg = "Error occurred while adding the watertank device '" + + device.getDeviceIdentifier() + "' to the watertank db."; + log.error(msg, e); + throw new DeviceMgtPluginException(msg, e); + } finally { + DeviceTypeUtils.cleanupResources(stmt, null); + } + return status; + } + + public boolean updateDevice(Device device) throws DeviceMgtPluginException { + boolean status = false; + Connection conn = null; + PreparedStatement stmt = null; + try { + conn = DeviceTypeDAO.getConnection(); + String updateDBQuery = + "UPDATE watertank_DEVICE SET DEVICE_NAME = ? WHERE watertank_DEVICE_ID = ?"; + stmt = conn.prepareStatement(updateDBQuery); + if (device.getProperties() == null) { + device.setProperties(new ArrayList()); + } + stmt.setString(1, device.getName()); + stmt.setString(2, device.getDeviceIdentifier()); + int rows = stmt.executeUpdate(); + if (rows > 0) { + status = true; + if (log.isDebugEnabled()) { + log.debug("watertank device " + device.getDeviceIdentifier() + " data has been" + + " modified."); + } + } + } catch (SQLException e) { + String msg = "Error occurred while modifying the watertank device '" + + device.getDeviceIdentifier() + "' data."; + log.error(msg, e); + throw new DeviceMgtPluginException(msg, e); + } finally { + DeviceTypeUtils.cleanupResources(stmt, null); + } + return status; + } + + public boolean deleteDevice(String deviceId) throws DeviceMgtPluginException { + boolean status = false; + Connection conn = null; + PreparedStatement stmt = null; + try { + conn = DeviceTypeDAO.getConnection(); + String deleteDBQuery = + "DELETE FROM watertank_DEVICE WHERE watertank_DEVICE_ID = ?"; + stmt = conn.prepareStatement(deleteDBQuery); + stmt.setString(1, deviceId); + int rows = stmt.executeUpdate(); + if (rows > 0) { + status = true; + if (log.isDebugEnabled()) { + log.debug("watertank device " + deviceId + " data has deleted" + + " from the watertank database."); + } + } + } catch (SQLException e) { + String msg = "Error occurred while deleting watertank device " + deviceId; + log.error(msg, e); + throw new DeviceMgtPluginException(msg, e); + } finally { + DeviceTypeUtils.cleanupResources(stmt, null); + } + return status; + } + + public List getAllDevices() throws DeviceMgtPluginException { + Connection conn = null; + PreparedStatement stmt = null; + ResultSet resultSet = null; + Device device; + List iotDevices = new ArrayList<>(); + try { + conn = DeviceTypeDAO.getConnection(); + String selectDBQuery = + "SELECT watertank_DEVICE_ID, DEVICE_NAME " + + "FROM watertank_DEVICE"; + stmt = conn.prepareStatement(selectDBQuery); + resultSet = stmt.executeQuery(); + while (resultSet.next()) { + device = new Device(); + device.setDeviceIdentifier(resultSet.getString(DeviceTypeConstants.DEVICE_PLUGIN_DEVICE_ID)); + device.setName(resultSet.getString(DeviceTypeConstants.DEVICE_PLUGIN_DEVICE_NAME)); + List propertyList = new ArrayList<>(); + device.setProperties(propertyList); + } + if (log.isDebugEnabled()) { + log.debug("All watertank device details have fetched from watertank database."); + } + return iotDevices; + } catch (SQLException e) { + String msg = "Error occurred while fetching all watertank device data'"; + log.error(msg, e); + throw new DeviceMgtPluginException(msg, e); + } finally { + DeviceTypeUtils.cleanupResources(stmt, resultSet); + DeviceTypeDAO.closeConnection(); + } + } +} diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/feature/DeviceTypeFeatureManager.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/feature/DeviceTypeFeatureManager.java new file mode 100644 index 00000000..bd1ecb97 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/feature/DeviceTypeFeatureManager.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) 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. + */ +package org.homeautomation.watertank.plugin.impl.feature; + +import org.homeautomation.watertank.plugin.constants.DeviceTypeConstants; +import org.wso2.carbon.device.mgt.common.DeviceManagementException; +import org.wso2.carbon.device.mgt.common.Feature; +import org.wso2.carbon.device.mgt.common.FeatureManager; +import org.wso2.carbon.device.mgt.extensions.feature.mgt.GenericFeatureManager; + +import java.util.List; + +public class DeviceTypeFeatureManager implements FeatureManager { + @Override + public boolean addFeature(Feature feature) throws DeviceManagementException { + return false; + } + + @Override + public boolean addFeatures(List features) throws DeviceManagementException { + return false; + } + + @Override + public Feature getFeature(String name) throws DeviceManagementException { + GenericFeatureManager genericFeatureManager = GenericFeatureManager.getInstance(); + return genericFeatureManager.getFeature(DeviceTypeConstants.DEVICE_TYPE, name); + } + + @Override + public List getFeatures() throws DeviceManagementException { + GenericFeatureManager genericFeatureManager = GenericFeatureManager.getInstance(); + return genericFeatureManager.getFeatures(DeviceTypeConstants.DEVICE_TYPE); + } + + @Override + public boolean removeFeature(String name) throws DeviceManagementException { + return false; + } + + @Override + public boolean addSupportedFeaturesToDB() throws DeviceManagementException { + return false; + } +} diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceSchemaInitializer.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceSchemaInitializer.java new file mode 100644 index 00000000..512d63a6 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceSchemaInitializer.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ +package org.homeautomation.watertank.plugin.impl.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.utils.CarbonUtils; +import org.wso2.carbon.utils.dbcreator.DatabaseCreator; + +import javax.sql.DataSource; +import java.io.File; + +/** + * Provides methods for initializing the database script. + */ +public class DeviceSchemaInitializer extends DatabaseCreator{ + + private static final Log log = LogFactory.getLog(DeviceSchemaInitializer.class); + private static final String setupSQLScriptBaseLocation = CarbonUtils.getCarbonHome() + File.separator + "dbscripts" + + File.separator + "cdm" + File.separator + "plugins" + File.separator; + + public DeviceSchemaInitializer(DataSource dataSource) { + super(dataSource); + } + + @Override + protected String getDbScriptLocation(String databaseType) { + String scriptName = databaseType + ".sql"; + if (log.isDebugEnabled()) { + log.debug("Loading database script from :" + scriptName); + } + return setupSQLScriptBaseLocation.replaceFirst("DBTYPE", databaseType) + scriptName; + } +} diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceTypeStartupListener.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceTypeStartupListener.java new file mode 100644 index 00000000..99c80fb5 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceTypeStartupListener.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.impl.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.core.ServerStartupObserver; + +import java.io.IOException; + +public class DeviceTypeStartupListener implements ServerStartupObserver { + private static final Log log = LogFactory.getLog(DeviceTypeStartupListener.class); + + @Override + public void completingServerStartup() { + } + + @Override + public void completedServerStartup() { + try { + DeviceTypeUtils.setupMqttOutputAdapter(); + } catch (IOException e) { + log.error("Failed to initialize the watertank output adapter", e); + } + } +} diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceTypeUtils.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceTypeUtils.java new file mode 100644 index 00000000..a69aa5b3 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/impl/util/DeviceTypeUtils.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.impl.util; + +import org.homeautomation.watertank.plugin.constants.DeviceTypeConstants; +import org.homeautomation.watertank.plugin.exception.DeviceMgtPluginException; +import org.homeautomation.watertank.plugin.internal.DeviceTypeManagementDataHolder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.core.util.Utils; +import org.wso2.carbon.event.output.adapter.core.MessageType; +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterConfiguration; +import org.wso2.carbon.event.output.adapter.core.exception.OutputEventAdapterException; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * Contains utility methods used by watertank plugin. + */ +public class DeviceTypeUtils { + + private static Log log = LogFactory.getLog(DeviceTypeUtils.class); + + public static void cleanupResources(Connection conn, PreparedStatement stmt, ResultSet rs) { + if (rs != null) { + try { + rs.close(); + } catch (SQLException e) { + log.warn("Error occurred while closing result set", e); + } + } + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + log.warn("Error occurred while closing prepared statement", e); + } + } + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + log.warn("Error occurred while closing database connection", e); + } + } + } + + public static void cleanupResources(PreparedStatement stmt, ResultSet rs) { + cleanupResources(null, stmt, rs); + } + + /** + * Creates the device management schema. + */ + public static void setupDeviceManagementSchema() throws DeviceMgtPluginException { + try { + Context ctx = new InitialContext(); + DataSource dataSource = (DataSource) ctx.lookup(DeviceTypeConstants.DATA_SOURCE_NAME); + DeviceSchemaInitializer initializer = + new DeviceSchemaInitializer(dataSource); + log.info("Initializing device management repository database schema"); + initializer.createRegistryDatabase(); + } catch (NamingException e) { + log.error("Error while looking up the data source: " + DeviceTypeConstants.DATA_SOURCE_NAME); + } catch (Exception e) { + throw new DeviceMgtPluginException("Error occurred while initializing Iot Device " + + "Management database schema", e); + } + } + + public static String replaceMqttProperty(String urlWithPlaceholders) { + String MQTT_BROKER_HOST = null; + String MQTT_PORT = null; + if(!DeviceTypeConstants.MQTT_BROKER_HOST.startsWith("$")){ + MQTT_BROKER_HOST = "\\$".concat(DeviceTypeConstants.MQTT_BROKER_HOST); + } + if(!DeviceTypeConstants.MQTT_PORT.startsWith("$")){ + MQTT_PORT = "\\$".concat(DeviceTypeConstants.MQTT_PORT); + } + urlWithPlaceholders = Utils.replaceSystemProperty(urlWithPlaceholders); + urlWithPlaceholders = urlWithPlaceholders.replaceAll(MQTT_PORT, "" + + (DeviceTypeConstants.DEFAULT_MQTT_PORT + getPortOffset())); + urlWithPlaceholders = urlWithPlaceholders.replaceAll(MQTT_BROKER_HOST, + System.getProperty(DeviceTypeConstants.DEFAULT_CARBON_LOCAL_IP_PROPERTY, "localhost")); + return urlWithPlaceholders; + } + + private static int getPortOffset() { + ServerConfiguration carbonConfig = ServerConfiguration.getInstance(); + String portOffset = System.getProperty("portOffset", carbonConfig.getFirstProperty( + DeviceTypeConstants.CARBON_CONFIG_PORT_OFFSET)); + try { + if ((portOffset != null)) { + return Integer.parseInt(portOffset.trim()); + } else { + return DeviceTypeConstants.CARBON_DEFAULT_PORT_OFFSET; + } + } catch (NumberFormatException e) { + return DeviceTypeConstants.CARBON_DEFAULT_PORT_OFFSET; + } + } + + public static void setupMqttOutputAdapter() throws IOException { + OutputEventAdapterConfiguration outputEventAdapterConfiguration = + createMqttOutputEventAdapterConfiguration(DeviceTypeConstants.MQTT_ADAPTER_NAME, + DeviceTypeConstants.MQTT_ADAPTER_TYPE, MessageType.TEXT); + try { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain( + DeviceTypeConstants.DEVICE_TYPE_PROVIDER_DOMAIN, true); + DeviceTypeManagementDataHolder.getInstance().getOutputEventAdapterService() + .create(outputEventAdapterConfiguration); + } catch (OutputEventAdapterException e) { + log.error("Unable to create Output Event Adapter : " + DeviceTypeConstants.MQTT_ADAPTER_NAME, e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + + /** + * Create Output Event Adapter Configuration for given configuration. + * + * @param name Output Event Adapter name + * @param type Output Event Adapter type + * @param msgFormat Output Event Adapter message format + * @return OutputEventAdapterConfiguration instance for given configuration + */ + private static OutputEventAdapterConfiguration createMqttOutputEventAdapterConfiguration(String name, String type, + String msgFormat) throws IOException { + OutputEventAdapterConfiguration outputEventAdapterConfiguration = new OutputEventAdapterConfiguration(); + outputEventAdapterConfiguration.setName(name); + outputEventAdapterConfiguration.setType(type); + outputEventAdapterConfiguration.setMessageFormat(msgFormat); + File configFile = new File(DeviceTypeConstants.MQTT_CONFIG_LOCATION); + if (configFile.exists()) { + Map mqttAdapterProperties = new HashMap<>(); + InputStream propertyStream = configFile.toURI().toURL().openStream(); + Properties properties = new Properties(); + properties.load(propertyStream); + mqttAdapterProperties.put(DeviceTypeConstants.USERNAME_PROPERTY_KEY, properties.getProperty( + DeviceTypeConstants.USERNAME_PROPERTY_KEY)); + mqttAdapterProperties.put(DeviceTypeConstants.DCR_PROPERTY_KEY, Utils.replaceSystemProperty( + properties.getProperty(DeviceTypeConstants.DCR_PROPERTY_KEY))); + mqttAdapterProperties.put(DeviceTypeConstants.BROKER_URL_PROPERTY_KEY, replaceMqttProperty( + properties.getProperty(DeviceTypeConstants.BROKER_URL_PROPERTY_KEY))); + mqttAdapterProperties.put(DeviceTypeConstants.SCOPES_PROPERTY_KEY, properties.getProperty( + DeviceTypeConstants.SCOPES_PROPERTY_KEY)); + mqttAdapterProperties.put(DeviceTypeConstants.CLEAR_SESSION_PROPERTY_KEY, properties.getProperty( + DeviceTypeConstants.CLEAR_SESSION_PROPERTY_KEY)); + mqttAdapterProperties.put(DeviceTypeConstants.QOS_PROPERTY_KEY, properties.getProperty( + DeviceTypeConstants.QOS_PROPERTY_KEY)); + mqttAdapterProperties.put(DeviceTypeConstants.CLIENT_ID_PROPERTY_KEY, ""); + mqttAdapterProperties.put(DeviceTypeConstants.RESOURCE, "output-event"); + outputEventAdapterConfiguration.setStaticProperties(mqttAdapterProperties); + } + return outputEventAdapterConfiguration; + } +} diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/internal/DeviceTypeManagementDataHolder.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/internal/DeviceTypeManagementDataHolder.java new file mode 100644 index 00000000..51d63863 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/internal/DeviceTypeManagementDataHolder.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.internal; + +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService; + +/** + * DataHolder class of watertank plugins component. + */ +public class DeviceTypeManagementDataHolder { + + private static DeviceTypeManagementDataHolder thisInstance = new DeviceTypeManagementDataHolder(); + private OutputEventAdapterService outputEventAdapterService; + + private DeviceTypeManagementDataHolder() { + } + + public static DeviceTypeManagementDataHolder getInstance() { + return thisInstance; + } + + public OutputEventAdapterService getOutputEventAdapterService() { + return outputEventAdapterService; + } + + public void setOutputEventAdapterService( + OutputEventAdapterService outputEventAdapterService) { + this.outputEventAdapterService = outputEventAdapterService; + } +} + diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/internal/ServiceComponent.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/internal/ServiceComponent.java new file mode 100644 index 00000000..2fe88c32 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/internal/ServiceComponent.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.internal; + +import org.homeautomation.watertank.plugin.impl.util.DeviceTypeStartupListener; +import org.homeautomation.watertank.plugin.exception.DeviceMgtPluginException; +import org.homeautomation.watertank.plugin.impl.util.DeviceTypeUtils; +import org.homeautomation.watertank.plugin.impl.DeviceTypeManagerService; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.core.ServerStartupObserver; +import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService; + +/** + * @scr.component name="ServiceComponent" + * immediate="true" + * @scr.reference name="event.output.adapter.service" + * interface="org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService" + * cardinality="1..1" + * policy="dynamic" + * bind="setOutputEventAdapterService" + * unbind="unsetOutputEventAdapterService" + */ + +public class ServiceComponent { + private static final Log log = LogFactory.getLog(ServiceComponent.class); + private ServiceRegistration serviceRegistration; + + protected void activate(ComponentContext ctx) { + if (log.isDebugEnabled()) { + log.debug("Activating b Management Service Component"); + } + try { + DeviceTypeManagerService deviceTypeManagerService = new DeviceTypeManagerService(); + BundleContext bundleContext = ctx.getBundleContext(); + serviceRegistration = + bundleContext.registerService(DeviceManagementService.class.getName(), + deviceTypeManagerService, null); + bundleContext.registerService(ServerStartupObserver.class.getName(), new DeviceTypeStartupListener(), + null); + String setupOption = System.getProperty("setup"); + if (setupOption != null) { + if (log.isDebugEnabled()) { + log.debug("-Dsetup is enabled. Iot Device management repository schema initialization is about " + + "to begin"); + } + try { + DeviceTypeUtils.setupDeviceManagementSchema(); + } catch (DeviceMgtPluginException e) { + log.error("Exception occurred while initializing device management database schema", e); + } + } + if (log.isDebugEnabled()) { + log.debug("b Management Service Component has been successfully activated"); + } + } catch (Throwable e) { + log.error("Error occurred while activating Current Sensor Management Service Component", e); + } + } + + protected void deactivate(ComponentContext ctx) { + if (log.isDebugEnabled()) { + log.debug("De-activating b Management Service Component"); + } + try { + if (serviceRegistration != null) { + serviceRegistration.unregister(); + } + if (log.isDebugEnabled()) { + log.debug("Current Sensor Management Service Component has been successfully de-activated"); + } + } catch (Throwable e) { + log.error("Error occurred while de-activating Iot Device Management bundle", e); + } + } + + /** + * Initialize the Output EventAdapter Service dependency + * + * @param outputEventAdapterService Output EventAdapter Service reference + */ + protected void setOutputEventAdapterService(OutputEventAdapterService outputEventAdapterService) { + DeviceTypeManagementDataHolder.getInstance().setOutputEventAdapterService(outputEventAdapterService); + } + + /** + * De-reference the Output EventAdapter Service dependency. + */ + protected void unsetOutputEventAdapterService(OutputEventAdapterService outputEventAdapterService) { + DeviceTypeManagementDataHolder.getInstance().setOutputEventAdapterService(null); + } +} diff --git a/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/mqtt/MqttConfig.java b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/mqtt/MqttConfig.java new file mode 100644 index 00000000..81df7f11 --- /dev/null +++ b/modules/samples/watertank/component/plugin/src/main/java/org/homeautomation/watertank/plugin/mqtt/MqttConfig.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.homeautomation.watertank.plugin.mqtt; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.homeautomation.watertank.plugin.constants.DeviceTypeConstants; +import org.homeautomation.watertank.plugin.impl.util.DeviceTypeUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class MqttConfig { + + private static String brokerEndpoint; + + private static MqttConfig mqttConfig = new MqttConfig(); + private static final Log log = LogFactory.getLog(MqttConfig.class); + + private MqttConfig() { + File configFile = new File(DeviceTypeConstants.MQTT_CONFIG_LOCATION); + if (configFile.exists()) { + try { + InputStream propertyStream = configFile.toURI().toURL().openStream(); + Properties properties = new Properties(); + properties.load(propertyStream); + brokerEndpoint = DeviceTypeUtils.replaceMqttProperty( + properties.getProperty(DeviceTypeConstants.BROKER_URL_PROPERTY_KEY)); + } catch (IOException e) { + log.error("Failed to read the mqtt.properties file" + e); + } + } + } + + public static MqttConfig getInstance() { + return mqttConfig; + } + + public String getBrokerEndpoint() { + return brokerEndpoint; + } +} diff --git a/modules/samples/watertank/component/pom.xml b/modules/samples/watertank/component/pom.xml new file mode 100644 index 00000000..52befd69 --- /dev/null +++ b/modules/samples/watertank/component/pom.xml @@ -0,0 +1,56 @@ + + + + + org.homeautomation + watertank + 1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + 1.0-SNAPSHOT + watertank-component + pom + + + + + org.apache.felix + maven-scr-plugin + ${maven-scr-plugin.version} + + + generate-scr-scrdescriptor + + scr + + + + + + + + + plugin + api + ui + analytics + + \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/pom.xml b/modules/samples/watertank/component/ui/pom.xml new file mode 100644 index 00000000..9b6a6d42 --- /dev/null +++ b/modules/samples/watertank/component/ui/pom.xml @@ -0,0 +1,56 @@ + + + + + org.homeautomation + watertank-component + 1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + ${project-base-package}.ui + ${project-base-package}.ui + pom + + + + maven-assembly-plugin + ${maven-assembly-plugin.version} + + ${project.artifactId}-1.0-SNAPSHOT + false + + src/assembly/src.xml + + + + + create-archive + package + + single + + + + + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/assembly/src.xml b/modules/samples/watertank/component/ui/src/assembly/src.xml new file mode 100644 index 00000000..522fbd8a --- /dev/null +++ b/modules/samples/watertank/component/ui/src/assembly/src.xml @@ -0,0 +1,37 @@ + + + + + src + + zip + + false + ${basedir}/src + + + ${basedir}/src/main/resources/jaggeryapps/devicemgt + / + true + + + \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.hbs b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.hbs new file mode 100644 index 00000000..526de0a3 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.hbs @@ -0,0 +1,50 @@ +{{! + Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + + WSO2 Inc. licenses this file to you 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. +}} + +
+
+ Temperature +
+
+
+
+
+
+
+
+
+ +
+ Humidity +
+
+
+
+
+
+
+
+
+ +
+ +{{#zone "bottomJs"}} + {{js "js/watertank.js"}} +{{/zone}} diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.js b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.js new file mode 100644 index 00000000..9a6b1620 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.js @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +function onRequest(context) { + var devices = context.unit.params.devices; + var deviceType = context.uriParams.deviceType; + var deviceId = request.getParameter("deviceId"); + + if (devices) { + return { + "devices": stringify(devices), + "backendApiUri": devicemgtProps["httpsURL"] + "/watertank/device/stats/" + }; + } else if (deviceType != null && deviceType != undefined && deviceId != null && deviceId != undefined) { + var deviceModule = require("/app/modules/device.js").deviceModule; + var device = deviceModule.viewDevice(deviceType, deviceId); + if (device && device.status != "error") { + return { + "device": device, + "backendApiUrl": devicemgtProps["httpsURL"] + "/watertank/device/stats/" + deviceId + "/sensors/" + }; + } else { + response.sendError(404, "Device Id " + deviceId + " of type " + deviceType + " cannot be found!"); + exit(); + } + } +} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.json b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.json new file mode 100644 index 00000000..688e9398 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/analytics-view.json @@ -0,0 +1,3 @@ +{ + "version": "1.0.0" +} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/public/js/watertank.js b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/public/js/watertank.js new file mode 100644 index 00000000..a92554f8 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.analytics-view/public/js/watertank.js @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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 palette = new Rickshaw.Color.Palette({scheme: "classic9"}); +var graphMap = {}; + +function drawGraph_watertank(from, to) { + var devices = $("#watertank-details").data("devices"); + var tzOffset = new Date().getTimezoneOffset() * 60; + + var streamIndex = 0; + var streams = ["temperature", "humidity"]; + + populateGraph(); + + function populateGraph() { + retrieveDataAndDrawLineGraph(streams[streamIndex], from, to); + streamIndex++; + } + + function clearContent(type) { + $("#y_axis-" + type).html(""); + $("#smoother-" + type).html(""); + $("#legend-" + type).html(""); + $("#chart-" + type).html(""); + $("#x_axis-" + type).html(""); + $("#slider-" + type).html(""); + } + + function initGraph(type) { + if (graphMap[type]) { + return graphMap[type]; + } + + var chartWrapperElmId = "#watertank-div-chart"; + var graphWidth = $(chartWrapperElmId).width() - 50; + + var graphConfig = { + element: document.getElementById("chart-" + type), + width: graphWidth, + height: 400, + strokeWidth: 2, + renderer: 'line', + interpolation: "linear", + unstack: true, + stack: false, + xScale: d3.time.scale(), + padding: {top: 0.2, left: 0.02, right: 0.02, bottom: 0.2}, + series: [] + }; + + if (devices) { + for (var i = 0; i < devices.length; i++) { + graphConfig['series'].push( + { + 'color': palette.color(), + 'data': [{ + x: parseInt(new Date().getTime() / 1000), + y: 0 + }], + 'name': devices[i].name + }); + } + } else { + graphConfig['series'].push( + { + 'color': palette.color(), + 'data': [{ + x: parseInt(new Date().getTime() / 1000), + y: 0 + }], + 'name': $("#watertank-details").data("devicename") + }); + } + + var graph = new Rickshaw.Graph(graphConfig); + graph.render(); + + var xAxis = new Rickshaw.Graph.Axis.Time({ + graph: graph + }); + xAxis.render(); + + var yAxis = new Rickshaw.Graph.Axis.Y({ + graph: graph, + orientation: 'left', + element: document.getElementById("y_axis-" + type), + width: 40, + height: 410 + }); + yAxis.render(); + + var slider = new Rickshaw.Graph.RangeSlider.Preview({ + graph: graph, + element: document.getElementById("slider-" + type) + }); + + var legend = new Rickshaw.Graph.Legend({ + graph: graph, + element: document.getElementById("legend-" + type) + }); + + var hoverDetail = new Rickshaw.Graph.HoverDetail({ + graph: graph, + formatter: function (series, x, y) { + var date = '' + + moment((x + tzOffset) * 1000).format('Do MMM YYYY h:mm:ss a') + ''; + var swatch = ''; + return swatch + series.name + ": " + parseInt(y) + '
' + date; + } + }); + + var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({ + graph: graph, + legend: legend + }); + + var order = new Rickshaw.Graph.Behavior.Series.Order({ + graph: graph, + legend: legend + }); + + var highlighter = new Rickshaw.Graph.Behavior.Series.Highlight({ + graph: graph, + legend: legend + }); + + graphMap[type] = {}; + graphMap[type].graph = graph; + graphMap[type].config = graphConfig; + return graphMap[type]; + } + + function retrieveDataAndDrawLineGraph(type, from, to) { + clearContent(type); + + var graphObj = initGraph(type); + var graph = graphObj.graph; + var graphConfig = graphObj.config; + + var deviceIndex = 0; + + if (devices) { + getData(); + } else { + var backendApiUrl = $("#watertank-div-chart").data("backend-api-url") + type + "?from=" + from + "&to=" + to; + var successCallback = function (data) { + if (data) { + drawLineGraph(JSON.parse(data)); + } + populateGraph(); + }; + invokerUtil.get(backendApiUrl, successCallback, function (message) { + console.log(message); + }); + } + + function getData() { + if (deviceIndex >= devices.length) { + return; + } + var backendApiUrl = $("#watertank-div-chart").data("backend-api-url") + devices[deviceIndex].deviceIdentifier + + "/sensors/" + type + "?from=" + from + "&to=" + to; + var successCallback = function (data) { + if (data) { + drawLineGraph(JSON.parse(data)); + } + deviceIndex++; + getData(); + }; + invokerUtil.get(backendApiUrl, successCallback, function (message) { + console.log(message); + deviceIndex++; + getData(); + }); + } + + function drawLineGraph(data) { + if (data.length === 0 || data.length === undefined) { + return; + } + + var chartData = []; + for (var i = 0; i < data.length; i++) { + chartData.push( + { + x: parseInt(data[i].values.time) - tzOffset, + y: parseInt(getFieldData(data[i], type)) + } + ); + } + + graphConfig.series[deviceIndex].data = chartData; + graph.update(); + } + } + + function getFieldData(data, type) { + var columnData; + switch (type) { + case "temperature" : + columnData = data.values.temperature; + break; + case "humidity" : + columnData = data.values.humidity; + break; + } + + return columnData; + } + +} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.hbs b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.hbs new file mode 100644 index 00000000..6786980c --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.hbs @@ -0,0 +1,68 @@ +{{#zone "topCss"}} + +{{/zone}} + +{{#zone "device-thumbnail"}} + +{{/zone}} + +{{#zone "device-opetations"}} +
+ Operations +
+
+ {{unit "iot.unit.device.operation-bar" device=device backendApiUri=backendApiUri autoCompleteParams=autoCompleteParams}} +
+{{/zone}} + +{{#zone "device-detail-properties"}} +
+ +
+
+ +
+
Device Statistics
+ {{unit "cdmf.unit.device.type.watertank.realtime.analytics-view" device=device}} +
+
+
Operations Log
+
+ +
+
+ Not available yet +
+
+
+
+
+
+
+
+{{/zone}} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.js b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.js new file mode 100644 index 00000000..422e9002 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +function onRequest(context) { + var log = new Log("cdmf.unit.device.type.watertank.device-view.js"); + var deviceType = context.uriParams.deviceType; + var deviceId = request.getParameter("id"); + var autoCompleteParams = [ + {"name": "deviceId", "value": deviceId} + ]; + + if (deviceType != null && deviceType != undefined && deviceId != null && deviceId != undefined) { + var deviceModule = require("/app/modules/device.js").deviceModule; + var device = deviceModule.viewDevice(deviceType, deviceId); + if (device && device.status != "error") { + return { + "device": device, + "backendApiUri": devicemgtProps["httpsURL"] + "/" + deviceType + "/", + "autoCompleteParams": autoCompleteParams + }; + } else { + response.sendError(404, "Device Id " + deviceId + " of type " + deviceType + " cannot be found!"); + exit(); + } + } +} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.json b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.json new file mode 100644 index 00000000..688e9398 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/device-view.json @@ -0,0 +1,3 @@ +{ + "version": "1.0.0" +} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/public/images/deviceType.png b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.device-view/public/images/deviceType.png new file mode 100644 index 0000000000000000000000000000000000000000..7c5808697e193b734c127fdf563f335ecd094bea GIT binary patch literal 28584 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 + +
+
+
Temperature
+
+
Time
+
+ +
+
Humidity
+
+
Time
+
+ +
+ + + + + View Device Analytics + + +{{#zone "bottomJs"}} + {{js "js/moment.min.js"}} + {{js "js/socket.io.min.js"}} + {{js "js/device-stats.js"}} +{{/zone}} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.js b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.js new file mode 100644 index 00000000..d231140b --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.js @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +function onRequest(context) { + var device = context.unit.params.device; + var devicemgtProps = require('/app/conf/devicemgt-props.js').config(); + var constants = require("/app/modules/constants.js"); + var websocketEndpoint = devicemgtProps["httpsURL"].replace("https", "wss"); + var tokenPair = session.get(constants.ACCESS_TOKEN_PAIR_IDENTIFIER); + var token = ""; + if (tokenPair) { + token = tokenPair.accessToken; + } + websocketEndpoint = websocketEndpoint + "/secured-outputui/org.wso2.iot.watertank/1.0.0?" + + "token=" + token + "&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type; + return {"device": device, "websocketEndpoint": websocketEndpoint}; +} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.json b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.json new file mode 100644 index 00000000..688e9398 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/analytics-view.json @@ -0,0 +1,3 @@ +{ + "version": "1.0.0" +} \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/device-stats.js b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/device-stats.js new file mode 100644 index 00000000..8314bed4 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/device-stats.js @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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 ws; + +var temperature; +var temperatureData = []; + +var humidity; +var humidityData = []; + +var palette = new Rickshaw.Color.Palette({scheme: "classic9"}); + +$(window).load(function () { + + temperature = lineGraph("temperature", temperatureData); + humidity = lineGraph("humidity", humidityData); + + var websocketUrl = $("#div-chart").data("websocketurl"); + connect(websocketUrl) +}); + +$(window).unload(function () { + disconnect(); +}); + +function lineGraph(type, chartData) { + var tNow = new Date().getTime() / 1000; + for (var i = 0; i < 30; i++) { + chartData.push({ + x: tNow - (30 - i) * 15, + y: parseFloat(0) + }); + } + + var graph = new Rickshaw.Graph({ + element: document.getElementById("chart-" + type), + width: $("#div-chart").width() - 50, + height: 300, + renderer: "line", + padding: {top: 0.2, left: 0.0, right: 0.0, bottom: 0.2}, + xScale: d3.time.scale(), + series: [{ + 'color': palette.color(), + 'data': chartData, + 'name': type && type[0].toUpperCase() + type.slice(1) + }] + }); + + graph.render(); + + var xAxis = new Rickshaw.Graph.Axis.Time({ + graph: graph + }); + + xAxis.render(); + + new Rickshaw.Graph.Axis.Y({ + graph: graph, + orientation: 'left', + height: 300, + tickFormat: Rickshaw.Fixtures.Number.formatKMBT, + element: document.getElementById('y_axis-' + type) + }); + + new Rickshaw.Graph.HoverDetail({ + graph: graph, + formatter: function (series, x, y) { + var date = '' + moment(x * 1000).format('Do MMM YYYY h:mm:ss a') + ''; + var swatch = ''; + return swatch + series.name + ": " + parseInt(y) + '
' + date; + } + }); + + return graph; +} + +//websocket connection +function connect(target) { + if ('WebSocket' in window) { + ws = new WebSocket(target); + } else if ('MozWebSocket' in window) { + ws = new MozWebSocket(target); + } else { + console.log('WebSocket is not supported by this browser.'); + } + if (ws) { + ws.onmessage = function (event) { + var dataPoint = JSON.parse(event.data); + if (dataPoint) { + var time = parseInt(dataPoint[0]) / 1000; + graphUpdate(temperatureData, time, dataPoint[3], temperature); + graphUpdate(humidityData, time, dataPoint[4], humidity); + } + }; + } +} + +function graphUpdate(chartData, xValue, yValue, graph) { + chartData.push({ + x: parseInt(xValue), + y: parseFloat(yValue) + }); + chartData.shift(); + graph.update(); +} + +function disconnect() { + if (ws != null) { + ws.close(); + ws = null; + } +} diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/moment.min.js b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/moment.min.js new file mode 100644 index 00000000..78e5aaad --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/moment.min.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +//! moment.js +//! version : 2.10.2 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com +!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return Ac.apply(null,arguments)}function b(a){Ac=a}function c(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function d(a){return"[object Array]"===Object.prototype.toString.call(a)}function e(a){return"[object Date]"===Object.prototype.toString.call(a)||a instanceof Date}function f(a,b){var c,d=[];for(c=0;c0)for(c in Cc)d=Cc[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function m(b){l(this,b),this._d=new Date(+b._d),Dc===!1&&(Dc=!0,a.updateOffset(this),Dc=!1)}function n(a){return a instanceof m||null!=a&&g(a,"_isAMomentObject")}function o(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function p(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;e>d;d++)(c&&a[d]!==b[d]||!c&&o(a[d])!==o(b[d]))&&g++;return g+f}function q(){}function r(a){return a?a.toLowerCase().replace("_","-"):a}function s(a){for(var b,c,d,e,f=0;f0;){if(d=t(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&p(e,c,!0)>=b-1)break;b--}f++}return null}function t(a){var b=null;if(!Ec[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=Bc._abbr,require("./locale/"+a),u(b)}catch(c){}return Ec[a]}function u(a,b){var c;return a&&(c="undefined"==typeof b?w(a):v(a,b),c&&(Bc=c)),Bc._abbr}function v(a,b){return null!==b?(b.abbr=a,Ec[a]||(Ec[a]=new q),Ec[a].set(b),u(a),Ec[a]):(delete Ec[a],null)}function w(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return Bc;if(!d(a)){if(b=t(a))return b;a=[a]}return s(a)}function x(a,b){var c=a.toLowerCase();Fc[c]=Fc[c+"s"]=Fc[b]=a}function y(a){return"string"==typeof a?Fc[a]||Fc[a.toLowerCase()]:void 0}function z(a){var b,c,d={};for(c in a)g(a,c)&&(b=y(c),b&&(d[b]=a[c]));return d}function A(b,c){return function(d){return null!=d?(C(this,b,d),a.updateOffset(this,c),this):B(this,b)}}function B(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function C(a,b,c){return a._d["set"+(a._isUTC?"UTC":"")+b](c)}function D(a,b){var c;if("object"==typeof a)for(c in a)this.set(c,a[c]);else if(a=y(a),"function"==typeof this[a])return this[a](b);return this}function E(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthb;b++)d[b]=Jc[d[b]]?Jc[d[b]]:G(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function I(a,b){return a.isValid()?(b=J(b,a.localeData()),Ic[b]||(Ic[b]=H(b)),Ic[b](a)):a.localeData().invalidDate()}function J(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Hc.lastIndex=0;d>=0&&Hc.test(a);)a=a.replace(Hc,c),Hc.lastIndex=0,d-=1;return a}function K(a,b,c){Yc[a]="function"==typeof b?b:function(a){return a&&c?c:b}}function L(a,b){return g(Yc,a)?Yc[a](b._strict,b._locale):new RegExp(M(a))}function M(a){return a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function N(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=o(a)}),c=0;cd;d++){if(e=i([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}}function U(a,b){var c;return"string"==typeof b&&(b=a.localeData().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),Q(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function V(b){return null!=b?(U(this,b),a.updateOffset(this,!0),this):B(this,"Month")}function W(){return Q(this.year(),this.month())}function X(a){var b,c=a._a;return c&&-2===a._pf.overflow&&(b=c[_c]<0||c[_c]>11?_c:c[ad]<1||c[ad]>Q(c[$c],c[_c])?ad:c[bd]<0||c[bd]>24||24===c[bd]&&(0!==c[cd]||0!==c[dd]||0!==c[ed])?bd:c[cd]<0||c[cd]>59?cd:c[dd]<0||c[dd]>59?dd:c[ed]<0||c[ed]>999?ed:-1,a._pf._overflowDayOfYear&&($c>b||b>ad)&&(b=ad),a._pf.overflow=b),a}function Y(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function Z(a,b){var c=!0;return h(function(){return c&&(Y(a),c=!1),b.apply(this,arguments)},b)}function $(a,b){hd[a]||(Y(b),hd[a]=!0)}function _(a){var b,c,d=a._i,e=id.exec(d);if(e){for(a._pf.iso=!0,b=0,c=jd.length;c>b;b++)if(jd[b][1].exec(d)){a._f=jd[b][0]+(e[6]||" ");break}for(b=0,c=kd.length;c>b;b++)if(kd[b][1].exec(d)){a._f+=kd[b][0];break}d.match(Vc)&&(a._f+="Z"),sa(a)}else a._isValid=!1}function aa(b){var c=ld.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(_(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))}function ba(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function ca(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function da(a){return ea(a)?366:365}function ea(a){return a%4===0&&a%100!==0||a%400===0}function fa(){return ea(this.year())}function ga(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=za(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function ha(a){return ga(a,this._week.dow,this._week.doy).week}function ia(){return this._week.dow}function ja(){return this._week.doy}function ka(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function la(a){var b=ga(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}function ma(a,b,c,d,e){var f,g,h=ca(a,0,1).getUTCDay();return h=0===h?7:h,c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:da(a-1)+g}}function na(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function oa(a,b,c){return null!=a?a:null!=b?b:c}function pa(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function qa(a){var b,c,d,e,f=[];if(!a._d){for(d=pa(a),a._w&&null==a._a[ad]&&null==a._a[_c]&&ra(a),a._dayOfYear&&(e=oa(a._a[$c],d[$c]),a._dayOfYear>da(e)&&(a._pf._overflowDayOfYear=!0),c=ca(e,0,a._dayOfYear),a._a[_c]=c.getUTCMonth(),a._a[ad]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=f[b]=d[b];for(;7>b;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];24===a._a[bd]&&0===a._a[cd]&&0===a._a[dd]&&0===a._a[ed]&&(a._nextDay=!0,a._a[bd]=0),a._d=(a._useUTC?ca:ba).apply(null,f),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[bd]=24)}}function ra(a){var b,c,d,e,f,g,h;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4,c=oa(b.GG,a._a[$c],ga(za(),1,4).year),d=oa(b.W,1),e=oa(b.E,1)):(f=a._locale._week.dow,g=a._locale._week.doy,c=oa(b.gg,a._a[$c],ga(za(),f,g).year),d=oa(b.w,1),null!=b.d?(e=b.d,f>e&&++d):e=null!=b.e?b.e+f:f),h=ma(c,d,e,g,f),a._a[$c]=h.year,a._dayOfYear=h.dayOfYear}function sa(b){if(b._f===a.ISO_8601)return void _(b);b._a=[],b._pf.empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=J(b._f,b._locale).match(Gc)||[],c=0;c0&&b._pf.unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length),Jc[f]?(d?b._pf.empty=!1:b._pf.unusedTokens.push(f),P(f,d,b)):b._strict&&!d&&b._pf.unusedTokens.push(f);b._pf.charsLeftOver=i-j,h.length>0&&b._pf.unusedInput.push(h),b._pf.bigHour===!0&&b._a[bd]<=12&&(b._pf.bigHour=void 0),b._a[bd]=ta(b._locale,b._a[bd],b._meridiem),qa(b),X(b)}function ta(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b}function ua(a){var b,d,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;fg)&&(e=g,d=b));h(a,d||b)}function va(a){if(!a._d){var b=z(a._i);a._a=[b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],qa(a)}}function wa(a){var b,c=a._i,e=a._f;return a._locale=a._locale||w(a._l),null===c||void 0===e&&""===c?k({nullInput:!0}):("string"==typeof c&&(a._i=c=a._locale.preparse(c)),n(c)?new m(X(c)):(d(e)?ua(a):e?sa(a):xa(a),b=new m(X(a)),b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b))}function xa(b){var c=b._i;void 0===c?b._d=new Date:e(c)?b._d=new Date(+c):"string"==typeof c?aa(b):d(c)?(b._a=f(c.slice(0),function(a){return parseInt(a,10)}),qa(b)):"object"==typeof c?va(b):"number"==typeof c?b._d=new Date(c):a.createFromInputFallback(b)}function ya(a,b,d,e,f){var g={};return"boolean"==typeof d&&(e=d,d=void 0),g._isAMomentObject=!0,g._useUTC=g._isUTC=f,g._l=d,g._i=a,g._f=b,g._strict=e,g._pf=c(),wa(g)}function za(a,b,c,d){return ya(a,b,c,d,!1)}function Aa(a,b){var c,e;if(1===b.length&&d(b[0])&&(b=b[0]),!b.length)return za();for(c=b[0],e=1;ea&&(a=-a,c="-"),c+E(~~(a/60),2)+b+E(~~a%60,2)})}function Ga(a){var b=(a||"").match(Vc)||[],c=b[b.length-1]||[],d=(c+"").match(qd)||["-",0,0],e=+(60*d[1])+o(d[2]);return"+"===d[0]?e:-e}function Ha(b,c){var d,f;return c._isUTC?(d=c.clone(),f=(n(b)||e(b)?+b:+za(b))-+d,d._d.setTime(+d._d+f),a.updateOffset(d,!1),d):za(b).local();return c._isUTC?za(b).zone(c._offset||0):za(b).local()}function Ia(a){return 15*-Math.round(a._d.getTimezoneOffset()/15)}function Ja(b,c){var d,e=this._offset||0;return null!=b?("string"==typeof b&&(b=Ga(b)),Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Ia(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?Za(this,Ua(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Ia(this)}function Ka(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function La(a){return this.utcOffset(0,a)}function Ma(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Ia(this),"m")),this}function Na(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(Ga(this._i)),this}function Oa(a){return a=a?za(a).utcOffset():0,(this.utcOffset()-a)%60===0}function Pa(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Qa(){if(this._a){var a=this._isUTC?i(this._a):za(this._a);return this.isValid()&&p(this._a,a.toArray())>0}return!1}function Ra(){return!this._isUTC}function Sa(){return this._isUTC}function Ta(){return this._isUTC&&0===this._offset}function Ua(a,b){var c,d,e,f=a,h=null;return Ea(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(h=rd.exec(a))?(c="-"===h[1]?-1:1,f={y:0,d:o(h[ad])*c,h:o(h[bd])*c,m:o(h[cd])*c,s:o(h[dd])*c,ms:o(h[ed])*c}):(h=sd.exec(a))?(c="-"===h[1]?-1:1,f={y:Va(h[2],c),M:Va(h[3],c),d:Va(h[4],c),h:Va(h[5],c),m:Va(h[6],c),s:Va(h[7],c),w:Va(h[8],c)}):null==f?f={}:"object"==typeof f&&("from"in f||"to"in f)&&(e=Xa(za(f.from),za(f.to)),f={},f.ms=e.milliseconds,f.M=e.months),d=new Da(f),Ea(a)&&g(a,"_locale")&&(d._locale=a._locale),d}function Va(a,b){var c=a&&parseFloat(a.replace(",","."));return(isNaN(c)?0:c)*b}function Wa(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Xa(a,b){var c;return b=Ha(b,a),a.isBefore(b)?c=Wa(a,b):(c=Wa(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c}function Ya(a,b){return function(c,d){var e,f;return null===d||isNaN(+d)||($(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period)."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Ua(c,d),Za(this,e,a),this}}function Za(b,c,d,e){var f=c._milliseconds,g=c._days,h=c._months;e=null==e?!0:e,f&&b._d.setTime(+b._d+f*d),g&&C(b,"Date",B(b,"Date")+g*d),h&&U(b,B(b,"Month")+h*d),e&&a.updateOffset(b,g||h)}function $a(a){var b=a||za(),c=Ha(b,this).startOf("day"),d=this.diff(c,"days",!0),e=-6>d?"sameElse":-1>d?"lastWeek":0>d?"lastDay":1>d?"sameDay":2>d?"nextDay":7>d?"nextWeek":"sameElse";return this.format(this.localeData().calendar(e,this,za(b)))}function _a(){return new m(this)}function ab(a,b){var c;return b=y("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=n(a)?a:za(a),+this>+a):(c=n(a)?+a:+za(a),c<+this.clone().startOf(b))}function bb(a,b){var c;return b=y("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=n(a)?a:za(a),+a>+this):(c=n(a)?+a:+za(a),+this.clone().endOf(b)a?Math.ceil(a):Math.floor(a)}function fb(a,b,c){var d,e,f=Ha(a,this),g=6e4*(f.utcOffset()-this.utcOffset());return b=y(b),"year"===b||"month"===b||"quarter"===b?(e=gb(this,f),"quarter"===b?e/=3:"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:eb(e)}function gb(a,b){var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()),f=a.clone().add(e,"months");return 0>b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)}function hb(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function ib(){var a=this.clone().utc();return 0b;b++)if(this._weekdaysParse[b]||(c=za([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b}function Jb(a){var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Eb(a,this.localeData()),this.add(a-b,"d")):b}function Kb(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function Lb(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)}function Mb(a,b){F(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}function Nb(a,b){return b._meridiemParse}function Ob(a){return"p"===(a+"").toLowerCase().charAt(0)}function Pb(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Qb(a){F(0,[a,3],0,"millisecond")}function Rb(){return this._isUTC?"UTC":""}function Sb(){return this._isUTC?"Coordinated Universal Time":""}function Tb(a){return za(1e3*a)}function Ub(){return za.apply(null,arguments).parseZone()}function Vb(a,b,c){var d=this._calendar[a];return"function"==typeof d?d.call(b,c):d}function Wb(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b}function Xb(){return this._invalidDate}function Yb(a){return this._ordinal.replace("%d",a)}function Zb(a){return a}function $b(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)}function _b(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)}function ac(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function bc(a,b,c,d){var e=w(),f=i().set(d,b);return e[c](f,a)}function cc(a,b,c,d,e){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return bc(a,b,c,e);var f,g=[];for(f=0;d>f;f++)g[f]=bc(a,f,c,e);return g}function dc(a,b){return cc(a,b,"months",12,"month")}function ec(a,b){return cc(a,b,"monthsShort",12,"month")}function fc(a,b){return cc(a,b,"weekdays",7,"day")}function gc(a,b){return cc(a,b,"weekdaysShort",7,"day")}function hc(a,b){return cc(a,b,"weekdaysMin",7,"day")}function ic(){var a=this._data;return this._milliseconds=Od(this._milliseconds),this._days=Od(this._days),this._months=Od(this._months),a.milliseconds=Od(a.milliseconds),a.seconds=Od(a.seconds),a.minutes=Od(a.minutes),a.hours=Od(a.hours),a.months=Od(a.months),a.years=Od(a.years),this}function jc(a,b,c,d){var e=Ua(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}function kc(a,b){return jc(this,a,b,1)}function lc(a,b){return jc(this,a,b,-1)}function mc(){var a,b,c,d=this._milliseconds,e=this._days,f=this._months,g=this._data,h=0;return g.milliseconds=d%1e3,a=eb(d/1e3),g.seconds=a%60,b=eb(a/60),g.minutes=b%60,c=eb(b/60),g.hours=c%24,e+=eb(c/24),h=eb(nc(e)),e-=eb(oc(h)),f+=eb(e/30),e%=30,h+=eb(f/12),f%=12,g.days=e,g.months=f,g.years=h,this}function nc(a){return 400*a/146097}function oc(a){return 146097*a/400}function pc(a){var b,c,d=this._milliseconds;if(a=y(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+12*nc(b),"month"===a?c:c/12;switch(b=this._days+Math.round(oc(this._months/12)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 24*b*60+d/6e4;case"second":return 24*b*60*60+d/1e3;case"millisecond":return Math.floor(24*b*60*60*1e3)+d;default:throw new Error("Unknown unit "+a)}}function qc(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*o(this._months/12)}function rc(a){return function(){return this.as(a)}}function sc(a){return a=y(a),this[a+"s"]()}function tc(a){return function(){return this._data[a]}}function uc(){return eb(this.days()/7)}function vc(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function wc(a,b,c){var d=Ua(a).abs(),e=ce(d.as("s")),f=ce(d.as("m")),g=ce(d.as("h")),h=ce(d.as("d")),i=ce(d.as("M")),j=ce(d.as("y")),k=e0,k[4]=c,vc.apply(null,k)}function xc(a,b){return void 0===de[a]?!1:void 0===b?de[a]:(de[a]=b,!0)}function yc(a){var b=this.localeData(),c=wc(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function zc(){var a=ee(this.years()),b=ee(this.months()),c=ee(this.days()),d=ee(this.hours()),e=ee(this.minutes()),f=ee(this.seconds()+this.milliseconds()/1e3),g=this.asSeconds();return g?(0>g?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"}var Ac,Bc,Cc=a.momentProperties=[],Dc=!1,Ec={},Fc={},Gc=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g,Hc=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Ic={},Jc={},Kc=/\d/,Lc=/\d\d/,Mc=/\d{3}/,Nc=/\d{4}/,Oc=/[+-]?\d{6}/,Pc=/\d\d?/,Qc=/\d{1,3}/,Rc=/\d{1,4}/,Sc=/[+-]?\d{1,6}/,Tc=/\d+/,Uc=/[+-]?\d+/,Vc=/Z|[+-]\d\d:?\d\d/gi,Wc=/[+-]?\d+(\.\d{1,3})?/,Xc=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Yc={},Zc={},$c=0,_c=1,ad=2,bd=3,cd=4,dd=5,ed=6;F("M",["MM",2],"Mo",function(){return this.month()+1}),F("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),F("MMMM",0,0,function(a){return this.localeData().months(this,a)}),x("month","M"),K("M",Pc),K("MM",Pc,Lc),K("MMM",Xc),K("MMMM",Xc),N(["M","MM"],function(a,b){b[_c]=o(a)-1}),N(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict);null!=e?b[_c]=e:c._pf.invalidMonth=a});var fd="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),gd="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),hd={};a.suppressDeprecationWarnings=!1;var id=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,jd=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],kd=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],ld=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=Z("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}),F(0,["YY",2],0,function(){return this.year()%100}),F(0,["YYYY",4],0,"year"),F(0,["YYYYY",5],0,"year"),F(0,["YYYYYY",6,!0],0,"year"),x("year","y"),K("Y",Uc),K("YY",Pc,Lc),K("YYYY",Rc,Nc),K("YYYYY",Sc,Oc),K("YYYYYY",Sc,Oc),N(["YYYY","YYYYY","YYYYYY"],$c),N("YY",function(b,c){c[$c]=a.parseTwoDigitYear(b)}),a.parseTwoDigitYear=function(a){return o(a)+(o(a)>68?1900:2e3)};var md=A("FullYear",!1);F("w",["ww",2],"wo","week"),F("W",["WW",2],"Wo","isoWeek"),x("week","w"),x("isoWeek","W"),K("w",Pc),K("ww",Pc,Lc),K("W",Pc),K("WW",Pc,Lc),O(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=o(a)});var nd={dow:0,doy:6};F("DDD",["DDDD",3],"DDDo","dayOfYear"),x("dayOfYear","DDD"),K("DDD",Qc),K("DDDD",Mc),N(["DDD","DDDD"],function(a,b,c){c._dayOfYear=o(a)}),a.ISO_8601=function(){};var od=Z("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(){var a=za.apply(null,arguments);return this>a?this:a}),pd=Z("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(){var a=za.apply(null,arguments);return a>this?this:a});Fa("Z",":"),Fa("ZZ",""),K("Z",Vc),K("ZZ",Vc),N(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=Ga(a)});var qd=/([\+\-]|\d\d)/gi;a.updateOffset=function(){};var rd=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,sd=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/;Ua.fn=Da.prototype;var td=Ya(1,"add"),ud=Ya(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ";var vd=Z("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)});F(0,["gg",2],0,function(){return this.weekYear()%100}),F(0,["GG",2],0,function(){return this.isoWeekYear()%100}),xb("gggg","weekYear"),xb("ggggg","weekYear"),xb("GGGG","isoWeekYear"),xb("GGGGG","isoWeekYear"),x("weekYear","gg"),x("isoWeekYear","GG"),K("G",Uc),K("g",Uc),K("GG",Pc,Lc),K("gg",Pc,Lc),K("GGGG",Rc,Nc),K("gggg",Rc,Nc),K("GGGGG",Sc,Oc),K("ggggg",Sc,Oc),O(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=o(a)}),O(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}),F("Q",0,0,"quarter"),x("quarter","Q"),K("Q",Kc),N("Q",function(a,b){b[_c]=3*(o(a)-1)}),F("D",["DD",2],"Do","date"),x("date","D"),K("D",Pc),K("DD",Pc,Lc),K("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),N(["D","DD"],ad),N("Do",function(a,b){b[ad]=o(a.match(Pc)[0],10)});var wd=A("Date",!0);F("d",0,"do","day"),F("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),F("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),F("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),F("e",0,0,"weekday"),F("E",0,0,"isoWeekday"),x("day","d"),x("weekday","e"),x("isoWeekday","E"),K("d",Pc),K("e",Pc),K("E",Pc),K("dd",Xc),K("ddd",Xc),K("dddd",Xc),O(["dd","ddd","dddd"],function(a,b,c){var d=c._locale.weekdaysParse(a);null!=d?b.d=d:c._pf.invalidWeekday=a}),O(["d","e","E"],function(a,b,c,d){b[d]=o(a)});var xd="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),yd="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),zd="Su_Mo_Tu_We_Th_Fr_Sa".split("_");F("H",["HH",2],0,"hour"),F("h",["hh",2],0,function(){return this.hours()%12||12}),Mb("a",!0),Mb("A",!1),x("hour","h"),K("a",Nb),K("A",Nb),K("H",Pc),K("h",Pc),K("HH",Pc,Lc),K("hh",Pc,Lc),N(["H","HH"],bd),N(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),N(["h","hh"],function(a,b,c){b[bd]=o(a),c._pf.bigHour=!0});var Ad=/[ap]\.?m?\.?/i,Bd=A("Hours",!0);F("m",["mm",2],0,"minute"),x("minute","m"),K("m",Pc),K("mm",Pc,Lc),N(["m","mm"],cd);var Cd=A("Minutes",!1);F("s",["ss",2],0,"second"),x("second","s"),K("s",Pc),K("ss",Pc,Lc),N(["s","ss"],dd);var Dd=A("Seconds",!1);F("S",0,0,function(){return~~(this.millisecond()/100)}),F(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),Qb("SSS"),Qb("SSSS"),x("millisecond","ms"),K("S",Qc,Kc),K("SS",Qc,Lc),K("SSS",Qc,Mc),K("SSSS",Tc),N(["S","SS","SSS","SSSS"],function(a,b){b[ed]=o(1e3*("0."+a))});var Ed=A("Milliseconds",!1);F("z",0,0,"zoneAbbr"),F("zz",0,0,"zoneName");var Fd=m.prototype;Fd.add=td,Fd.calendar=$a,Fd.clone=_a,Fd.diff=fb,Fd.endOf=pb,Fd.format=jb,Fd.from=kb,Fd.fromNow=lb,Fd.get=D,Fd.invalidAt=wb,Fd.isAfter=ab,Fd.isBefore=bb,Fd.isBetween=cb,Fd.isSame=db,Fd.isValid=ub,Fd.lang=vd,Fd.locale=mb,Fd.localeData=nb,Fd.max=pd,Fd.min=od,Fd.parsingFlags=vb,Fd.set=D,Fd.startOf=ob,Fd.subtract=ud,Fd.toArray=tb,Fd.toDate=sb,Fd.toISOString=ib,Fd.toJSON=ib,Fd.toString=hb,Fd.unix=rb,Fd.valueOf=qb,Fd.year=md,Fd.isLeapYear=fa,Fd.weekYear=zb,Fd.isoWeekYear=Ab,Fd.quarter=Fd.quarters=Db,Fd.month=V,Fd.daysInMonth=W,Fd.week=Fd.weeks=ka,Fd.isoWeek=Fd.isoWeeks=la,Fd.weeksInYear=Cb,Fd.isoWeeksInYear=Bb,Fd.date=wd,Fd.day=Fd.days=Jb,Fd.weekday=Kb,Fd.isoWeekday=Lb,Fd.dayOfYear=na,Fd.hour=Fd.hours=Bd,Fd.minute=Fd.minutes=Cd,Fd.second=Fd.seconds=Dd,Fd.millisecond=Fd.milliseconds=Ed,Fd.utcOffset=Ja,Fd.utc=La,Fd.local=Ma,Fd.parseZone=Na,Fd.hasAlignedHourOffset=Oa,Fd.isDST=Pa,Fd.isDSTShifted=Qa,Fd.isLocal=Ra,Fd.isUtcOffset=Sa,Fd.isUtc=Ta,Fd.isUTC=Ta,Fd.zoneAbbr=Rb,Fd.zoneName=Sb,Fd.dates=Z("dates accessor is deprecated. Use date instead.",wd),Fd.months=Z("months accessor is deprecated. Use month instead",V),Fd.years=Z("years accessor is deprecated. Use year instead",md),Fd.zone=Z("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",Ka);var Gd=Fd,Hd={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Id={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},Jd="Invalid date",Kd="%d",Ld=/\d{1,2}/,Md={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},Nd=q.prototype;Nd._calendar=Hd,Nd.calendar=Vb,Nd._longDateFormat=Id,Nd.longDateFormat=Wb,Nd._invalidDate=Jd,Nd.invalidDate=Xb,Nd._ordinal=Kd,Nd.ordinal=Yb,Nd._ordinalParse=Ld, +Nd.preparse=Zb,Nd.postformat=Zb,Nd._relativeTime=Md,Nd.relativeTime=$b,Nd.pastFuture=_b,Nd.set=ac,Nd.months=R,Nd._months=fd,Nd.monthsShort=S,Nd._monthsShort=gd,Nd.monthsParse=T,Nd.week=ha,Nd._week=nd,Nd.firstDayOfYear=ja,Nd.firstDayOfWeek=ia,Nd.weekdays=Fb,Nd._weekdays=xd,Nd.weekdaysMin=Hb,Nd._weekdaysMin=zd,Nd.weekdaysShort=Gb,Nd._weekdaysShort=yd,Nd.weekdaysParse=Ib,Nd.isPM=Ob,Nd._meridiemParse=Ad,Nd.meridiem=Pb,u("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===o(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),a.lang=Z("moment.lang is deprecated. Use moment.locale instead.",u),a.langData=Z("moment.langData is deprecated. Use moment.localeData instead.",w);var Od=Math.abs,Pd=rc("ms"),Qd=rc("s"),Rd=rc("m"),Sd=rc("h"),Td=rc("d"),Ud=rc("w"),Vd=rc("M"),Wd=rc("y"),Xd=tc("milliseconds"),Yd=tc("seconds"),Zd=tc("minutes"),$d=tc("hours"),_d=tc("days"),ae=tc("months"),be=tc("years"),ce=Math.round,de={s:45,m:45,h:22,d:26,M:11},ee=Math.abs,fe=Da.prototype;fe.abs=ic,fe.add=kc,fe.subtract=lc,fe.as=pc,fe.asMilliseconds=Pd,fe.asSeconds=Qd,fe.asMinutes=Rd,fe.asHours=Sd,fe.asDays=Td,fe.asWeeks=Ud,fe.asMonths=Vd,fe.asYears=Wd,fe.valueOf=qc,fe._bubble=mc,fe.get=sc,fe.milliseconds=Xd,fe.seconds=Yd,fe.minutes=Zd,fe.hours=$d,fe.days=_d,fe.weeks=uc,fe.months=ae,fe.years=be,fe.humanize=yc,fe.toISOString=zc,fe.toString=zc,fe.toJSON=zc,fe.locale=mb,fe.localeData=nb,fe.toIsoString=Z("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",zc),fe.lang=vd,F("X",0,0,"unix"),F("x",0,0,"valueOf"),K("x",Uc),K("X",Wc),N("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),N("x",function(a,b,c){c._d=new Date(o(a))}),a.version="2.10.2",b(za),a.fn=Gd,a.min=Ba,a.max=Ca,a.utc=i,a.unix=Tb,a.months=dc,a.isDate=e,a.locale=u,a.invalid=k,a.duration=Ua,a.isMoment=n,a.weekdays=fc,a.parseZone=Ub,a.localeData=w,a.isDuration=Ea,a.monthsShort=ec,a.weekdaysMin=hc,a.defineLocale=v,a.weekdaysShort=gc,a.normalizeUnits=y,a.relativeTimeThreshold=xc;var ge=a;return ge}); \ No newline at end of file diff --git a/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/socket.io.min.js b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/socket.io.min.js new file mode 100644 index 00000000..7e870c98 --- /dev/null +++ b/modules/samples/watertank/component/ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.watertank.realtime.analytics-view/public/js/socket.io.min.js @@ -0,0 +1,2 @@ +/*! Socket.IO.min.js build:0.9.16, production. Copyright(c) 2011 LearnBoost MIT Licensed */ +var io="undefined"==typeof module?{}:module.exports;(function(){(function(a,b){var c=a;c.version="0.9.16",c.protocol=1,c.transports=[],c.j=[],c.sockets={},c.connect=function(a,d){var e=c.util.parseUri(a),f,g;b&&b.location&&(e.protocol=e.protocol||b.location.protocol.slice(0,-1),e.host=e.host||(b.document?b.document.domain:b.location.hostname),e.port=e.port||b.location.port),f=c.util.uniqueUri(e);var h={host:e.host,secure:"https"==e.protocol,port:e.port||("https"==e.protocol?443:80),query:e.query||""};c.util.merge(h,d);if(h["force new connection"]||!c.sockets[f])g=new c.Socket(h);return!h["force new connection"]&&g&&(c.sockets[f]=g),g=g||c.sockets[f],g.of(e.path.length>1?e.path:"")}})("object"==typeof module?module.exports:this.io={},this),function(a,b){var c=a.util={},d=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,e=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];c.parseUri=function(a){var b=d.exec(a||""),c={},f=14;while(f--)c[e[f]]=b[f]||"";return c},c.uniqueUri=function(a){var c=a.protocol,d=a.host,e=a.port;return"document"in b?(d=d||document.domain,e=e||(c=="https"&&document.location.protocol!=="https:"?443:document.location.port)):(d=d||"localhost",!e&&c=="https"&&(e=443)),(c||"http")+"://"+d+":"+(e||80)},c.query=function(a,b){var d=c.chunkQuery(a||""),e=[];c.merge(d,c.chunkQuery(b||""));for(var f in d)d.hasOwnProperty(f)&&e.push(f+"="+d[f]);return e.length?"?"+e.join("&"):""},c.chunkQuery=function(a){var b={},c=a.split("&"),d=0,e=c.length,f;for(;db.length?a:b,f=a.length>b.length?b:a;for(var g=0,h=f.length;g0&&a.splice(0,1)[0]!=c.transport.name);a.length?h(a):c.publish("connect_failed")}}},c.options["connect timeout"]))})}c.sessionid=d,c.closeTimeout=f*1e3,c.heartbeatTimeout=e*1e3,c.transports||(c.transports=c.origTransports=g?b.util.intersect(g.split(","),c.options.transports):c.options.transports),c.setHeartbeatTimeout(),h(c.transports),c.once("connect",function(){clearTimeout(c.connectTimeoutTimer),a&&typeof a=="function"&&a()})}),this},d.prototype.setHeartbeatTimeout=function(){clearTimeout(this.heartbeatTimeoutTimer);if(this.transport&&!this.transport.heartbeats())return;var a=this;this.heartbeatTimeoutTimer=setTimeout(function(){a.transport.onClose()},this.heartbeatTimeout)},d.prototype.packet=function(a){return this.connected&&!this.doBuffer?this.transport.packet(a):this.buffer.push(a),this},d.prototype.setBuffer=function(a){this.doBuffer=a,!a&&this.connected&&this.buffer.length&&(this.options.manualFlush||this.flushBuffer())},d.prototype.flushBuffer=function(){this.transport.payload(this.buffer),this.buffer=[]},d.prototype.disconnect=function(){if(this.connected||this.connecting)this.open&&this.of("").packet({type:"disconnect"}),this.onDisconnect("booted");return this},d.prototype.disconnectSync=function(){var a=b.util.request(),c=["http"+(this.options.secure?"s":"")+":/",this.options.host+":"+this.options.port,this.options.resource,b.protocol,"",this.sessionid].join("/")+"/?disconnect=1";a.open("GET",c,!1),a.send(null),this.onDisconnect("booted")},d.prototype.isXDomain=function(){var a=c.location.port||("https:"==c.location.protocol?443:80);return this.options.host!==c.location.hostname||this.options.port!=a},d.prototype.onConnect=function(){this.connected||(this.connected=!0,this.connecting=!1,this.doBuffer||this.setBuffer(!1),this.emit("connect"))},d.prototype.onOpen=function(){this.open=!0},d.prototype.onClose=function(){this.open=!1,clearTimeout(this.heartbeatTimeoutTimer)},d.prototype.onPacket=function(a){this.of(a.endpoint).onPacket(a)},d.prototype.onError=function(a){a&&a.advice&&a.advice==="reconnect"&&(this.connected||this.connecting)&&(this.disconnect(),this.options.reconnect&&this.reconnect()),this.publish("error",a&&a.reason?a.reason:a)},d.prototype.onDisconnect=function(a){var b=this.connected,c=this.connecting;this.connected=!1,this.connecting=!1,this.open=!1;if(b||c)this.transport.close(),this.transport.clearTimeouts(),b&&(this.publish("disconnect",a),"booted"!=a&&this.options.reconnect&&!this.reconnecting&&this.reconnect())},d.prototype.reconnect=function(){function e(){if(a.connected){for(var b in a.namespaces)a.namespaces.hasOwnProperty(b)&&""!==b&&a.namespaces[b].packet({type:"connect"});a.publish("reconnect",a.transport.name,a.reconnectionAttempts)}clearTimeout(a.reconnectionTimer),a.removeListener("connect_failed",f),a.removeListener("connect",f),a.reconnecting=!1,delete a.reconnectionAttempts,delete a.reconnectionDelay,delete a.reconnectionTimer,delete a.redoTransports,a.options["try multiple transports"]=c}function f(){if(!a.reconnecting)return;if(a.connected)return e();if(a.connecting&&a.reconnecting)return a.reconnectionTimer=setTimeout(f,1e3);a.reconnectionAttempts++>=b?a.redoTransports?(a.publish("reconnect_failed"),e()):(a.on("connect_failed",f),a.options["try multiple transports"]=!0,a.transports=a.origTransports,a.transport=a.getTransport(),a.redoTransports=!0,a.connect()):(a.reconnectionDelay=10:!1},c.xdomainCheck=function(){return!0},typeof window!="undefined"&&(WEB_SOCKET_DISABLE_AUTO_INITIALIZATION=!0),b.transports.push("flashsocket")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports);if("undefined"!=typeof window)var swfobject=function(){function A(){if(t)return;try{var a=i.getElementsByTagName("body")[0].appendChild(Q("span"));a.parentNode.removeChild(a)}catch(b){return}t=!0;var c=l.length;for(var d=0;d0)for(var c=0;c0){var g=P(d);if(g)if(S(m[c].swfVersion)&&!(y.wk&&y.wk<312))U(d,!0),e&&(f.success=!0,f.ref=G(d),e(f));else if(m[c].expressInstall&&H()){var h={};h.data=m[c].expressInstall,h.width=g.getAttribute("width")||"0",h.height=g.getAttribute("height")||"0",g.getAttribute("class")&&(h.styleclass=g.getAttribute("class")),g.getAttribute("align")&&(h.align=g.getAttribute("align"));var i={},j=g.getElementsByTagName("param"),k=j.length;for(var l=0;l');h.outerHTML='"+k+"",n[n.length]=c.id,g=P(c.id)}else{var m=Q(b);m.setAttribute("type",e);for(var o in c)c[o]!=Object.prototype[o]&&(o.toLowerCase()=="styleclass"?m.setAttribute("class",c[o]):o.toLowerCase()!="classid"&&m.setAttribute(o,c[o]));for(var p in d)d[p]!=Object.prototype[p]&&p.toLowerCase()!="movie"&&M(m,p,d[p]);h.parentNode.replaceChild(m,h),g=m}}return g}function M(a,b,c){var d=Q("param");d.setAttribute("name",b),d.setAttribute("value",c),a.appendChild(d)}function N(a){var b=P(a);b&&b.nodeName=="OBJECT"&&(y.ie&&y.win?(b.style.display="none",function(){b.readyState==4?O(a):setTimeout(arguments.callee,10)}()):b.parentNode.removeChild(b))}function O(a){var b=P(a);if(b){for(var c in b)typeof b[c]=="function"&&(b[c]=null);b.parentNode.removeChild(b)}}function P(a){var b=null;try{b=i.getElementById(a)}catch(c){}return b}function Q(a){return i.createElement(a)}function R(a,b,c){a.attachEvent(b,c),o[o.length]=[a,b,c]}function S(a){var b=y.pv,c=a.split(".");return c[0]=parseInt(c[0],10),c[1]=parseInt(c[1],10)||0,c[2]=parseInt(c[2],10)||0,b[0]>c[0]||b[0]==c[0]&&b[1]>c[1]||b[0]==c[0]&&b[1]==c[1]&&b[2]>=c[2]?!0:!1}function T(c,d,e,f){if(y.ie&&y.mac)return;var g=i.getElementsByTagName("head")[0];if(!g)return;var h=e&&typeof e=="string"?e:"screen";f&&(v=null,w=null);if(!v||w!=h){var j=Q("style");j.setAttribute("type","text/css"),j.setAttribute("media",h),v=g.appendChild(j),y.ie&&y.win&&typeof i.styleSheets!=a&&i.styleSheets.length>0&&(v=i.styleSheets[i.styleSheets.length-1]),w=h}y.ie&&y.win?v&&typeof v.addRule==b&&v.addRule(c,d):v&&typeof i.createTextNode!=a&&v.appendChild(i.createTextNode(c+" {"+d+"}"))}function U(a,b){if(!x)return;var c=b?"visible":"hidden";t&&P(a)?P(a).style.visibility=c:T("#"+a,"visibility:"+c)}function V(b){var c=/[\\\"<>\.;]/,d=c.exec(b)!=null;return d&&typeof encodeURIComponent!=a?encodeURIComponent(b):b}var a="undefined",b="object",c="Shockwave Flash",d="ShockwaveFlash.ShockwaveFlash",e="application/x-shockwave-flash",f="SWFObjectExprInst",g="onreadystatechange",h=window,i=document,j=navigator,k=!1,l=[D],m=[],n=[],o=[],p,q,r,s,t=!1,u=!1,v,w,x=!0,y=function(){var f=typeof i.getElementById!=a&&typeof i.getElementsByTagName!=a&&typeof i.createElement!=a,g=j.userAgent.toLowerCase(),l=j.platform.toLowerCase(),m=l?/win/.test(l):/win/.test(g),n=l?/mac/.test(l):/mac/.test(g),o=/webkit/.test(g)?parseFloat(g.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):!1,p=!1,q=[0,0,0],r=null;if(typeof j.plugins!=a&&typeof j.plugins[c]==b)r=j.plugins[c].description,r&&(typeof j.mimeTypes==a||!j.mimeTypes[e]||!!j.mimeTypes[e].enabledPlugin)&&(k=!0,p=!1,r=r.replace(/^.*\s+(\S+\s+\S+$)/,"$1"),q[0]=parseInt(r.replace(/^(.*)\..*$/,"$1"),10),q[1]=parseInt(r.replace(/^.*\.(.*)\s.*$/,"$1"),10),q[2]=/[a-zA-Z]/.test(r)?parseInt(r.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0);else if(typeof h[["Active"].concat("Object").join("X")]!=a)try{var s=new(window[["Active"].concat("Object").join("X")])(d);s&&(r=s.GetVariable("$version"),r&&(p=!0,r=r.split(" ")[1].split(","),q=[parseInt(r[0],10),parseInt(r[1],10),parseInt(r[2],10)]))}catch(t){}return{w3:f,pv:q,wk:o,ie:p,win:m,mac:n}}(),z=function(){if(!y.w3)return;(typeof i.readyState!=a&&i.readyState=="complete"||typeof i.readyState==a&&(i.getElementsByTagName("body")[0]||i.body))&&A(),t||(typeof i.addEventListener!=a&&i.addEventListener("DOMContentLoaded",A,!1),y.ie&&y.win&&(i.attachEvent(g,function(){i.readyState=="complete"&&(i.detachEvent(g,arguments.callee),A())}),h==top&&function(){if(t)return;try{i.documentElement.doScroll("left")}catch(a){setTimeout(arguments.callee,0);return}A()}()),y.wk&&function(){if(t)return;if(!/loaded|complete/.test(i.readyState)){setTimeout(arguments.callee,0);return}A()}(),C(A))}(),W=function(){y.ie&&y.win&&window.attachEvent("onunload",function(){var a=o.length;for(var b=0;b= 10.0.0 is required.");return}location.protocol=="file:"&&a.error("WARNING: web-socket-js doesn't work in file:///... URL unless you set Flash Security Settings properly. Open the page via Web server i.e. http://..."),WebSocket=function(a,b,c,d,e){var f=this;f.__id=WebSocket.__nextId++,WebSocket.__instances[f.__id]=f,f.readyState=WebSocket.CONNECTING,f.bufferedAmount=0,f.__events={},b?typeof b=="string"&&(b=[b]):b=[],setTimeout(function(){WebSocket.__addTask(function(){WebSocket.__flash.create(f.__id,a,b,c||null,d||0,e||null)})},0)},WebSocket.prototype.send=function(a){if(this.readyState==WebSocket.CONNECTING)throw"INVALID_STATE_ERR: Web Socket connection has not been established";var b=WebSocket.__flash.send(this.__id,encodeURIComponent(a));return b<0?!0:(this.bufferedAmount+=b,!1)},WebSocket.prototype.close=function(){if(this.readyState==WebSocket.CLOSED||this.readyState==WebSocket.CLOSING)return;this.readyState=WebSocket.CLOSING,WebSocket.__flash.close(this.__id)},WebSocket.prototype.addEventListener=function(a,b,c){a in this.__events||(this.__events[a]=[]),this.__events[a].push(b)},WebSocket.prototype.removeEventListener=function(a,b,c){if(!(a in this.__events))return;var d=this.__events[a];for(var e=d.length-1;e>=0;--e)if(d[e]===b){d.splice(e,1);break}},WebSocket.prototype.dispatchEvent=function(a){var b=this.__events[a.type]||[];for(var c=0;c"),this.doc.close(),this.doc.parentWindow.s=this;var a=this.doc.createElement("div");a.className="socketio",this.doc.body.appendChild(a),this.iframe=this.doc.createElement("iframe"),a.appendChild(this.iframe);var c=this,d=b.util.query(this.socket.options.query,"t="+ +(new Date));this.iframe.src=this.prepareUrl()+d,b.util.on(window,"unload",function(){c.destroy()})},c.prototype._=function(a,b){a=a.replace(/\\\//g,"/"),this.onData(a);try{var c=b.getElementsByTagName("script")[0];c.parentNode.removeChild(c)}catch(d){}},c.prototype.destroy=function(){if(this.iframe){try{this.iframe.src="about:blank"}catch(a){}this.doc=null,this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,CollectGarbage()}},c.prototype.close=function(){return this.destroy(),b.Transport.XHR.prototype.close.call(this)},c.check=function(a){if(typeof window!="undefined"&&["Active"].concat("Object").join("X")in window)try{var c=new(window[["Active"].concat("Object").join("X")])("htmlfile");return c&&b.Transport.XHR.check(a)}catch(d){}return!1},c.xdomainCheck=function(){return!1},b.transports.push("htmlfile")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports),function(a,b,c){function d(){b.Transport.XHR.apply(this,arguments)}function e(){}a["xhr-polling"]=d,b.util.inherit(d,b.Transport.XHR),b.util.merge(d,b.Transport.XHR),d.prototype.name="xhr-polling",d.prototype.heartbeats=function(){return!1},d.prototype.open=function(){var a=this;return b.Transport.XHR.prototype.open.call(a),!1},d.prototype.get=function(){function b(){this.readyState==4&&(this.onreadystatechange=e,this.status==200?(a.onData(this.responseText),a.get()):a.onClose())}function d(){this.onload=e,this.onerror=e,a.retryCounter=1,a.onData(this.responseText),a.get()}function f(){a.retryCounter++,!a.retryCounter||a.retryCounter>3?a.onClose():a.get()}if(!this.isOpen)return;var a=this;this.xhr=this.request(),c.XDomainRequest&&this.xhr instanceof XDomainRequest?(this.xhr.onload=d,this.xhr.onerror=f):this.xhr.onreadystatechange=b,this.xhr.send(null)},d.prototype.onClose=function(){b.Transport.XHR.prototype.onClose.call(this);if(this.xhr){this.xhr.onreadystatechange=this.xhr.onload=this.xhr.onerror=e;try{this.xhr.abort()}catch(a){}this.xhr=null}},d.prototype.ready=function(a,c){var d=this;b.util.defer(function(){c.call(d)})},b.transports.push("xhr-polling")}("undefined"!=typeof io?io.Transport:module.exports,"undefined"!=typeof io?io:module.parent.exports,this),function(a,b,c){function e(a){b.Transport["xhr-polling"].apply(this,arguments),this.index=b.j.length;var c=this;b.j.push(function(a){c._(a)})}var d=c.document&&"MozAppearance"in c.document.documentElement.style;a["jsonp-polling"]=e,b.util.inherit(e,b.Transport["xhr-polling"]),e.prototype.name="jsonp-polling",e.prototype.post=function(a){function i(){j(),c.socket.setBuffer(!1)}function j(){c.iframe&&c.form.removeChild(c.iframe);try{h=document.createElement('