From eca52912cc3763f79f1d0d5914f1bfb3fbf59e4c Mon Sep 17 00:00:00 2001 From: Shabirmean Date: Wed, 16 Dec 2015 11:47:44 +0530 Subject: [PATCH] Added RPi plugin component and feature def --- .../pom.xml | 127 ++ .../constants/RaspberrypiConstants.java | 27 + .../plugin/impl/RaspberrypiManager.java | 281 ++++ .../impl/RaspberrypiManagerService.java | 113 ++ .../plugin/impl/dao/RaspberrypiDAO.java | 123 ++ .../dao/impl/RaspberrypiDeviceDAOImpl.java | 238 ++++ .../plugin/impl/util/RaspberrypiUtils.java | 45 + ...RaspberrypiManagementServiceComponent.java | 104 ++ components/device-mgt-iot-raspberrypi/pom.xml | 62 + .../src/main/resources/configs/arduino.xml | 2 +- .../datasources/arduino-datasources.xml | 4 +- .../main/resources/conf/devicetype-config.xml | 13 +- .../pom.xml | 142 ++ .../src/main/resources/agent/raspberrypi.deb | 0 .../main/resources/agent/sketch.properties | 20 + .../src/main/resources/build.properties | 1 + .../main/resources/configs/raspberrypi.xml | 24 + .../datasources/raspberrypi-datasources.xml | 48 + .../resources/dbscripts/h2_raspberrypi.sql | 11 + .../resources/dbscripts/mysql_raspberrypi.sql | 12 + .../device-view.hbs | 126 ++ .../device-view.js | 43 + .../device-view.json | 3 + .../public/images/thumb.png | Bin 0 -> 62544 bytes .../public/js/device-detail.js | 197 +++ .../public/js/load-map.js | 55 + .../public/templates/applications-list.hbs | 12 + .../public/templates/operations-log.hbs | 24 + .../public/templates/policy-compliance.hbs | 79 ++ .../private/conf/device-type.json | 8 + .../public/images/thunb.png | Bin 0 -> 8006 bytes .../public/js/download.js | 174 +++ .../public/js/jquery.validate.js | 1220 +++++++++++++++++ .../type-view.hbs | 242 ++++ .../type-view.json | 3 + .../src/main/resources/p2.inf | 13 + .../pom.xml | 41 + pom.xml | 16 + 38 files changed, 3645 insertions(+), 8 deletions(-) create mode 100644 components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/pom.xml create mode 100644 components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/constants/RaspberrypiConstants.java create mode 100644 components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/RaspberrypiManager.java create mode 100644 components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/RaspberrypiManagerService.java create mode 100644 components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/dao/RaspberrypiDAO.java create mode 100644 components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/dao/impl/RaspberrypiDeviceDAOImpl.java create mode 100644 components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/util/RaspberrypiUtils.java create mode 100644 components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/internal/RaspberrypiManagementServiceComponent.java create mode 100644 components/device-mgt-iot-raspberrypi/pom.xml create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/pom.xml create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/agent/raspberrypi.deb create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/agent/sketch.properties create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/build.properties create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/configs/raspberrypi.xml create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/datasources/raspberrypi-datasources.xml create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/dbscripts/h2_raspberrypi.sql create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/dbscripts/mysql_raspberrypi.sql create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.hbs create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.js create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.json create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/images/thumb.png create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/js/device-detail.js create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/js/load-map.js create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/applications-list.hbs create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/operations-log.hbs create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/policy-compliance.hbs create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/private/conf/device-type.json create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/public/images/thunb.png create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/public/js/download.js create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/public/js/jquery.validate.js create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/type-view.hbs create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/type-view.json create mode 100644 features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/p2.inf create mode 100644 features/device-mgt-iot-raspberrypi-feature/pom.xml diff --git a/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/pom.xml b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/pom.xml new file mode 100644 index 0000000000..fe9eb006a2 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/pom.xml @@ -0,0 +1,127 @@ + + + + + + + device-mgt-iot-raspberrypi + org.wso2.carbon.devicemgt-plugins + 1.9.2-SNAPSHOT + ../pom.xml + + + 4.0.0 + org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl + 1.9.2-SNAPSHOT + bundle + WSO2 Carbon - IoT Server RaspberryPi Management Plugin + WSO2 Carbon - RaspberryPi Management/Control Plugin Implementation + http://wso2.org + + + + + org.apache.felix + maven-scr-plugin + + + maven-compiler-plugin + + 1.7 + 1.7 + + 2.3.2 + + + org.apache.felix + maven-bundle-plugin + 1.4.0 + true + + + ${project.artifactId} + ${project.artifactId} + ${carbon.iot.device.mgt.version} + IoT Server Impl Bundle + org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.internal + + org.osgi.framework, + org.osgi.service.component, + org.apache.commons.logging, + javax.xml.bind.*, + javax.naming, + javax.sql, + javax.xml.bind.annotation.*, + javax.xml.parsers, + javax.net, + javax.net.ssl, + org.w3c.dom, + org.wso2.carbon.device.mgt.common.*, + org.wso2.carbon.device.mgt.common, + org.wso2.carbon.context.*, + org.wso2.carbon.ndatasource.core, + org.wso2.carbon.device.mgt.iot.*, + + + + !org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.internal, + org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.* + + + + + + + + + + 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 + org.wso2.carbon.ndatasource.core + + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.iot + + + + + + + + \ No newline at end of file diff --git a/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/constants/RaspberrypiConstants.java b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/constants/RaspberrypiConstants.java new file mode 100644 index 0000000000..8a8841d0a1 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/constants/RaspberrypiConstants.java @@ -0,0 +1,27 @@ +/* + * 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.wso2.carbon.device.mgt.iot.raspberrypi.plugin.constants; + +public class RaspberrypiConstants { + + public final static String DEVICE_TYPE = "raspberrypi"; + public final static String DEVICE_PLUGIN_DEVICE_NAME = "DEVICE_NAME"; + public final static String DEVICE_PLUGIN_DEVICE_ID = "RASPBERRYPI_DEVICE_ID"; + +} diff --git a/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/RaspberrypiManager.java b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/RaspberrypiManager.java new file mode 100644 index 0000000000..57209ed0c5 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/RaspberrypiManager.java @@ -0,0 +1,281 @@ +/* + * 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.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.IotDeviceManagementDAOException; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.IotDeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dto.IotDevice; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.util.IotDeviceManagementUtil; +import org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl.dao.RaspberrypiDAO; + +import java.util.ArrayList; +import java.util.List; + + +/** + * This represents the Raspberrypi implementation of DeviceManagerService. + */ +public class RaspberrypiManager implements DeviceManager { + + private static final IotDeviceManagementDAOFactory iotDeviceManagementDAOFactory = new RaspberrypiDAO(); + private static final Log log = LogFactory.getLog(RaspberrypiManager.class); + + + + @Override + public FeatureManager getFeatureManager() { + return null; + } + + @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; + IotDevice iotDevice = IotDeviceManagementUtil.convertToIotDevice(device); + try { + if (log.isDebugEnabled()) { + log.debug("Enrolling a new Raspberrypi device : " + device.getDeviceIdentifier()); + } + RaspberrypiDAO.beginTransaction(); + status = iotDeviceManagementDAOFactory.getIotDeviceDAO().addIotDevice( + iotDevice); + RaspberrypiDAO.commitTransaction(); + } catch (IotDeviceManagementDAOException e) { + try { + RaspberrypiDAO.rollbackTransaction(); + } catch (IotDeviceManagementDAOException iotDAOEx) { + String msg = "Error occurred while roll back the device enrol transaction :" + device.toString(); + log.warn(msg, iotDAOEx); + } + String msg = "Error while enrolling the Raspberrypi device : " + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return status; + } + + @Override + public boolean modifyEnrollment(Device device) throws DeviceManagementException { + boolean status; + IotDevice iotDevice = IotDeviceManagementUtil.convertToIotDevice(device); + try { + if (log.isDebugEnabled()) { + log.debug("Modifying the Raspberrypi device enrollment data"); + } + RaspberrypiDAO.beginTransaction(); + status = iotDeviceManagementDAOFactory.getIotDeviceDAO() + .updateIotDevice(iotDevice); + RaspberrypiDAO.commitTransaction(); + } catch (IotDeviceManagementDAOException e) { + try { + RaspberrypiDAO.rollbackTransaction(); + } catch (IotDeviceManagementDAOException 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 Raspberrypi 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 Raspberrypi device : " + deviceId); + } + RaspberrypiDAO.beginTransaction(); + status = iotDeviceManagementDAOFactory.getIotDeviceDAO() + .deleteIotDevice(deviceId.getId()); + RaspberrypiDAO.commitTransaction(); + } catch (IotDeviceManagementDAOException e) { + try { + RaspberrypiDAO.rollbackTransaction(); + } catch (IotDeviceManagementDAOException 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 Raspberrypi 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 Raspberrypi device : " + deviceId.getId()); + } + IotDevice iotDevice = + iotDeviceManagementDAOFactory.getIotDeviceDAO().getIotDevice( + deviceId.getId()); + if (iotDevice != null) { + isEnrolled = true; + } + } catch (IotDeviceManagementDAOException e) { + String msg = "Error while checking the enrollment status of Raspberrypi 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 Raspberrypi device : " + deviceId.getId()); + } + IotDevice iotDevice = iotDeviceManagementDAOFactory.getIotDeviceDAO(). + getIotDevice(deviceId.getId()); + device = IotDeviceManagementUtil.convertToDevice(iotDevice); + } catch (IotDeviceManagementDAOException e) { + String msg = "Error while fetching the Raspberrypi 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 false; + } + + @Override + public boolean updateDeviceInfo(DeviceIdentifier deviceIdentifier, Device device) throws DeviceManagementException { + boolean status; + IotDevice iotDevice = IotDeviceManagementUtil.convertToIotDevice(device); + try { + if (log.isDebugEnabled()) { + log.debug( + "updating the details of Raspberrypi device : " + deviceIdentifier); + } + RaspberrypiDAO.beginTransaction(); + status = iotDeviceManagementDAOFactory.getIotDeviceDAO() + .updateIotDevice(iotDevice); + RaspberrypiDAO.commitTransaction(); + } catch (IotDeviceManagementDAOException e) { + try { + RaspberrypiDAO.rollbackTransaction(); + } catch (IotDeviceManagementDAOException 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 Raspberrypi device : " + deviceIdentifier; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return status; + } + + @Override + public List getAllDevices() throws DeviceManagementException { + List devices = null; + try { + if (log.isDebugEnabled()) { + log.debug("Fetching the details of all Raspberrypi devices"); + } + List iotDevices = + iotDeviceManagementDAOFactory.getIotDeviceDAO().getAllIotDevices(); + if (iotDevices != null) { + devices = new ArrayList(); + for (IotDevice iotDevice : iotDevices) { + devices.add(IotDeviceManagementUtil.convertToDevice(iotDevice)); + } + } + } catch (IotDeviceManagementDAOException e) { + String msg = "Error while fetching all Raspberrypi devices."; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + return devices; + } +} \ No newline at end of file diff --git a/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/RaspberrypiManagerService.java b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/RaspberrypiManagerService.java new file mode 100644 index 0000000000..474e87b2c1 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/RaspberrypiManagerService.java @@ -0,0 +1,113 @@ +/* + * 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.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl; + +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.app.mgt.Application; +import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException; +import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager; +import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; +import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.constants.RaspberrypiConstants; + +import java.util.List; + +public class RaspberrypiManagerService implements DeviceManagementService { + + private DeviceManager deviceManager; + + @Override + public String getType() { + return RaspberrypiConstants.DEVICE_TYPE; + } + + @Override + public String getProviderTenantDomain() { + return "carbon.super"; + } + + @Override + public boolean isSharedWithAllTenants() { + return true; + } + + @Override + public String[] getSharedTenantsDomain() { + return new String[0]; + } + + @Override + public void init() throws DeviceManagementException { + deviceManager = new RaspberrypiManager(); + } + + @Override + public DeviceManager getDeviceManager() { + return deviceManager; + } + + @Override + public ApplicationManager getApplicationManager() { + return null; + } + + @Override + public void notifyOperationToDevices(Operation operation, List deviceIds) + throws DeviceManagementException { + + } + + @Override + public Application[] getApplications(String domain, int pageNumber, int size) + throws ApplicationManagementException { + return new Application[0]; + } + + @Override + public void updateApplicationStatus(DeviceIdentifier deviceId, Application application, + String status) throws ApplicationManagementException { + + } + + @Override + public String getApplicationStatus(DeviceIdentifier deviceId, Application application) + throws ApplicationManagementException { + return null; + } + + @Override + public void installApplicationForDevices(Operation operation, List deviceIdentifiers) + throws ApplicationManagementException { + + } + + @Override + public void installApplicationForUsers(Operation operation, List userNameList) + throws ApplicationManagementException { + + } + + @Override + public void installApplicationForUserRoles(Operation operation, List userRoleList) + throws ApplicationManagementException { + + } +} diff --git a/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/dao/RaspberrypiDAO.java b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/dao/RaspberrypiDAO.java new file mode 100644 index 0000000000..ad81eaeae0 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/dao/RaspberrypiDAO.java @@ -0,0 +1,123 @@ +/* + * 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.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.IotDeviceDAO; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.IotDeviceManagementDAOException; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.IotDeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.IotDeviceManagementDAOFactoryInterface; +import org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.constants.RaspberrypiConstants; +import org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl.dao.impl.RaspberrypiDeviceDAOImpl; + + + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public class RaspberrypiDAO extends IotDeviceManagementDAOFactory implements IotDeviceManagementDAOFactoryInterface { + + private static final Log log = LogFactory.getLog(RaspberrypiDAO.class); + static DataSource dataSource; + private static ThreadLocal currentConnection = new ThreadLocal(); + + public RaspberrypiDAO() { + initRaspberrypiDAO(); + } + + @Override public IotDeviceDAO getIotDeviceDAO() { + return new RaspberrypiDeviceDAOImpl(); + } + + public static void initRaspberrypiDAO() { + dataSource = getDataSourceMap().get(RaspberrypiConstants.DEVICE_TYPE); + } + + public static void beginTransaction() throws IotDeviceManagementDAOException { + try { + Connection conn = dataSource.getConnection(); + conn.setAutoCommit(false); + currentConnection.set(conn); + } catch (SQLException e) { + throw new IotDeviceManagementDAOException("Error occurred while retrieving datasource connection", e); + } + } + + public static Connection getConnection() throws IotDeviceManagementDAOException { + if (currentConnection.get() == null) { + try { + currentConnection.set(dataSource.getConnection()); + } catch (SQLException e) { + throw new IotDeviceManagementDAOException("Error occurred while retrieving data source connection", e); + } + } + return currentConnection.get(); + } + + public static void commitTransaction() throws IotDeviceManagementDAOException { + 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 IotDeviceManagementDAOException("Error occurred while committing the transaction", e); + } finally { + closeConnection(); + } + } + + public static void closeConnection() throws IotDeviceManagementDAOException { + + 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 IotDeviceManagementDAOException { + 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 IotDeviceManagementDAOException("Error occurred while rollback the transaction", e); + } finally { + closeConnection(); + } + } +} \ No newline at end of file diff --git a/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/dao/impl/RaspberrypiDeviceDAOImpl.java b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/dao/impl/RaspberrypiDeviceDAOImpl.java new file mode 100644 index 0000000000..217c1298e6 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/dao/impl/RaspberrypiDeviceDAOImpl.java @@ -0,0 +1,238 @@ +/* + * 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.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl.dao.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.IotDeviceDAO; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.IotDeviceManagementDAOException; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dao.util.IotDeviceManagementDAOUtil; +import org.wso2.carbon.device.mgt.iot.util.iotdevice.dto.IotDevice; +import org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl.dao.RaspberrypiDAO; +import org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.constants.RaspberrypiConstants; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Implements IotDeviceDAO for Raspberrypi Devices. + */ +public class RaspberrypiDeviceDAOImpl implements IotDeviceDAO{ + + + private static final Log log = LogFactory.getLog(RaspberrypiDeviceDAOImpl.class); + + @Override + public IotDevice getIotDevice(String iotDeviceId) + throws IotDeviceManagementDAOException { + Connection conn = null; + PreparedStatement stmt = null; + IotDevice iotDevice = null; + ResultSet resultSet = null; + try { + conn = RaspberrypiDAO.getConnection(); + String selectDBQuery = + "SELECT RASPBERRYPI_DEVICE_ID, DEVICE_NAME" + + " FROM RASPBERRYPI_DEVICE WHERE RASPBERRYPI_DEVICE_ID = ?"; + stmt = conn.prepareStatement(selectDBQuery); + stmt.setString(1, iotDeviceId); + resultSet = stmt.executeQuery(); + + if (resultSet.next()) { + iotDevice = new IotDevice(); + iotDevice.setIotDeviceName(resultSet.getString( + RaspberrypiConstants.DEVICE_PLUGIN_DEVICE_NAME)); + Map propertyMap = new HashMap(); + + + + iotDevice.setDeviceProperties(propertyMap); + + if (log.isDebugEnabled()) { + log.debug("Raspberrypi device " + iotDeviceId + " data has been fetched from " + + "Raspberrypi database."); + } + } + } catch (SQLException e) { + String msg = "Error occurred while fetching Raspberrypi device : '" + iotDeviceId + "'"; + log.error(msg, e); + throw new IotDeviceManagementDAOException(msg, e); + } finally { + IotDeviceManagementDAOUtil.cleanupResources(stmt, resultSet); + RaspberrypiDAO.closeConnection(); + } + + return iotDevice; + } + + @Override + public boolean addIotDevice(IotDevice iotDevice) + throws IotDeviceManagementDAOException { + boolean status = false; + Connection conn = null; + PreparedStatement stmt = null; + try { + conn = RaspberrypiDAO.getConnection(); + String createDBQuery = + "INSERT INTO RASPBERRYPI_DEVICE(RASPBERRYPI_DEVICE_ID, DEVICE_NAME) VALUES (?, ?)"; + + stmt = conn.prepareStatement(createDBQuery); + stmt.setString(1, iotDevice.getIotDeviceId()); + stmt.setString(2,iotDevice.getIotDeviceName()); + if (iotDevice.getDeviceProperties() == null) { + iotDevice.setDeviceProperties(new HashMap()); + } + + + int rows = stmt.executeUpdate(); + if (rows > 0) { + status = true; + if (log.isDebugEnabled()) { + log.debug("Raspberrypi device " + iotDevice.getIotDeviceId() + " data has been" + + " added to the Raspberrypi database."); + } + } + } catch (SQLException e) { + String msg = "Error occurred while adding the Raspberrypi device '" + + iotDevice.getIotDeviceId() + "' to the Raspberrypi db."; + log.error(msg, e); + throw new IotDeviceManagementDAOException(msg, e); + } finally { + IotDeviceManagementDAOUtil.cleanupResources(stmt, null); + } + return status; + } + + @Override + public boolean updateIotDevice(IotDevice iotDevice) + throws IotDeviceManagementDAOException { + boolean status = false; + Connection conn = null; + PreparedStatement stmt = null; + try { + conn = RaspberrypiDAO.getConnection(); + String updateDBQuery = + "UPDATE RASPBERRYPI_DEVICE SET DEVICE_NAME = ? WHERE RASPBERRYPI_DEVICE_ID = ?"; + + stmt = conn.prepareStatement(updateDBQuery); + + if (iotDevice.getDeviceProperties() == null) { + iotDevice.setDeviceProperties(new HashMap()); + } + stmt.setString(1, iotDevice.getIotDeviceName()); + + stmt.setString(2, iotDevice.getIotDeviceId()); + int rows = stmt.executeUpdate(); + if (rows > 0) { + status = true; + if (log.isDebugEnabled()) { + log.debug("Raspberrypi device " + iotDevice.getIotDeviceId() + " data has been" + + " modified."); + } + } + } catch (SQLException e) { + String msg = "Error occurred while modifying the Raspberrypi device '" + + iotDevice.getIotDeviceId() + "' data."; + log.error(msg, e); + throw new IotDeviceManagementDAOException(msg, e); + } finally { + IotDeviceManagementDAOUtil.cleanupResources(stmt, null); + } + return status; + } + + @Override + public boolean deleteIotDevice(String iotDeviceId) + throws IotDeviceManagementDAOException { + boolean status = false; + Connection conn = null; + PreparedStatement stmt = null; + try { + conn = RaspberrypiDAO.getConnection(); + String deleteDBQuery = + "DELETE FROM RASPBERRYPI_DEVICE WHERE RASPBERRYPI_DEVICE_ID = ?"; + stmt = conn.prepareStatement(deleteDBQuery); + stmt.setString(1, iotDeviceId); + int rows = stmt.executeUpdate(); + if (rows > 0) { + status = true; + if (log.isDebugEnabled()) { + log.debug("Raspberrypi device " + iotDeviceId + " data has deleted" + + " from the Raspberrypi database."); + } + } + } catch (SQLException e) { + String msg = "Error occurred while deleting Raspberrypi device " + iotDeviceId; + log.error(msg, e); + throw new IotDeviceManagementDAOException(msg, e); + } finally { + IotDeviceManagementDAOUtil.cleanupResources(stmt, null); + } + return status; + } + + @Override + public List getAllIotDevices() + throws IotDeviceManagementDAOException { + + Connection conn = null; + PreparedStatement stmt = null; + ResultSet resultSet = null; + IotDevice iotDevice; + List iotDevices = new ArrayList(); + + try { + conn = RaspberrypiDAO.getConnection(); + String selectDBQuery = + "SELECT RASPBERRYPI_DEVICE_ID, DEVICE_NAME " + + "FROM RASPBERRYPI_DEVICE"; + stmt = conn.prepareStatement(selectDBQuery); + resultSet = stmt.executeQuery(); + while (resultSet.next()) { + iotDevice = new IotDevice(); + iotDevice.setIotDeviceId(resultSet.getString(RaspberrypiConstants.DEVICE_PLUGIN_DEVICE_ID)); + iotDevice.setIotDeviceName(resultSet.getString(RaspberrypiConstants.DEVICE_PLUGIN_DEVICE_NAME)); + + Map propertyMap = new HashMap(); + + iotDevice.setDeviceProperties(propertyMap); + iotDevices.add(iotDevice); + } + if (log.isDebugEnabled()) { + log.debug("All Raspberrypi device details have fetched from Raspberrypi database."); + } + return iotDevices; + } catch (SQLException e) { + String msg = "Error occurred while fetching all Raspberrypi device data'"; + log.error(msg, e); + throw new IotDeviceManagementDAOException(msg, e); + } finally { + IotDeviceManagementDAOUtil.cleanupResources(stmt, resultSet); + RaspberrypiDAO.closeConnection(); + } + + } + + } \ No newline at end of file diff --git a/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/util/RaspberrypiUtils.java b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/util/RaspberrypiUtils.java new file mode 100644 index 0000000000..efb7d7bc73 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/impl/util/RaspberrypiUtils.java @@ -0,0 +1,45 @@ +/* + * 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.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.Map; + +/** + * Contains utility methods used by Raspberrypi plugin. + */ +public class RaspberrypiUtils { + + private static Log log = LogFactory.getLog(RaspberrypiUtils.class); + + public static String getDeviceProperty(Map deviceProperties, String property) { + + String deviceProperty = deviceProperties.get(property); + + if (deviceProperty == null) { + return ""; + } + + return deviceProperty; + } + + +} diff --git a/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/internal/RaspberrypiManagementServiceComponent.java b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/internal/RaspberrypiManagementServiceComponent.java new file mode 100644 index 0000000000..5cbcdec359 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl/src/main/java/org/wso2/carbon/device/mgt/iot/raspberrypi/plugin/internal/RaspberrypiManagementServiceComponent.java @@ -0,0 +1,104 @@ +/* + * 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.wso2.carbon.device.mgt.iot.raspberrypi.plugin.internal; + +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.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.device.mgt.iot.service.DeviceTypeService; +import org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl.RaspberrypiManagerService; + + +/** + * @scr.component name="org.wso2.carbon.device.mgt.iot.raspberrypi.internal.RaspberrypiManagementServiceComponent" + * immediate="true" + * @scr.reference name="org.wso2.carbon.device.mgt.iot.service.DeviceTypeServiceImpl" + * interface="org.wso2.carbon.device.mgt.iot.service.DeviceTypeService" + * cardinality="1..1" + * policy="dynamic" + * bind="setDeviceTypeService" + * unbind="unsetDeviceTypeService" + */ +public class RaspberrypiManagementServiceComponent { + + + private ServiceRegistration raspberrypiServiceRegRef; + + + private static final Log log = LogFactory.getLog(RaspberrypiManagementServiceComponent.class); + + protected void activate(ComponentContext ctx) { + if (log.isDebugEnabled()) { + log.debug("Activating Raspberrypi Device Management Service Component"); + } + try { + BundleContext bundleContext = ctx.getBundleContext(); + raspberrypiServiceRegRef = + bundleContext.registerService(DeviceManagementService.class.getName(), + new RaspberrypiManagerService(), null); + if (log.isDebugEnabled()) { + log.debug( + "Raspberrypi Device Management Service Component has been successfully " + + "activated"); + } + } catch (Throwable e) { + log.error( + "Error occurred while activating Raspberrypi Device Management Service " + + "Component", + e); + } + } + + protected void deactivate(ComponentContext ctx) { + if (log.isDebugEnabled()) { + log.debug("De-activating Raspberrypi Device Management Service Component"); + } + try { + if (raspberrypiServiceRegRef != null) { + raspberrypiServiceRegRef.unregister(); + } + + if (log.isDebugEnabled()) { + log.debug( + "Raspberrypi Device Management Service Component has been successfully " + + "de-activated"); + } + } catch (Throwable e) { + log.error("Error occurred while de-activating Raspberrypi Device Management bundle", + e); + } + } + + protected void setDeviceTypeService(DeviceTypeService deviceTypeService) { + /* This is to avoid this component getting initialized before the + common registered */ + if (log.isDebugEnabled()) { + log.debug("Data source service set to mobile service component"); + } + } + + protected void unsetDeviceTypeService(DeviceTypeService deviceTypeService) { + //do nothing + } + + +} diff --git a/components/device-mgt-iot-raspberrypi/pom.xml b/components/device-mgt-iot-raspberrypi/pom.xml new file mode 100644 index 0000000000..80de139dd2 --- /dev/null +++ b/components/device-mgt-iot-raspberrypi/pom.xml @@ -0,0 +1,62 @@ + + + + + + + org.wso2.carbon.devicemgt-plugins + carbon-device-mgt-plugins-parent + 1.9.2-SNAPSHOT + ../../pom.xml + + + 4.0.0 + device-mgt-iot-raspberrypi + 1.9.2-SNAPSHOT + pom + WSO2 Carbon - IoT Device RaspberryPi Management Component + http://wso2.org + + + org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl + + + + + + + + org.apache.felix + maven-scr-plugin + 1.7.2 + + + generate-scr-scrdescriptor + + scr + + + + + + + + \ No newline at end of file diff --git a/features/device-mgt-iot-arduino-feature/org.wso2.carbon.device.mgt.iot.arduino.feature/src/main/resources/configs/arduino.xml b/features/device-mgt-iot-arduino-feature/org.wso2.carbon.device.mgt.iot.arduino.feature/src/main/resources/configs/arduino.xml index c6adf5d565..68cca71075 100644 --- a/features/device-mgt-iot-arduino-feature/org.wso2.carbon.device.mgt.iot.arduino.feature/src/main/resources/configs/arduino.xml +++ b/features/device-mgt-iot-arduino-feature/org.wso2.carbon.device.mgt.iot.arduino.feature/src/main/resources/configs/arduino.xml @@ -19,7 +19,7 @@ - jdbc/Arduino_DB + jdbc/ArduinoDM_DB diff --git a/features/device-mgt-iot-arduino-feature/org.wso2.carbon.device.mgt.iot.arduino.feature/src/main/resources/datasources/arduino-datasources.xml b/features/device-mgt-iot-arduino-feature/org.wso2.carbon.device.mgt.iot.arduino.feature/src/main/resources/datasources/arduino-datasources.xml index 4c6d53263f..767bee7fbb 100644 --- a/features/device-mgt-iot-arduino-feature/org.wso2.carbon.device.mgt.iot.arduino.feature/src/main/resources/datasources/arduino-datasources.xml +++ b/features/device-mgt-iot-arduino-feature/org.wso2.carbon.device.mgt.iot.arduino.feature/src/main/resources/datasources/arduino-datasources.xml @@ -24,13 +24,13 @@ Arduino_DB - The datasource used for the Virtual-Firealarm database + The datasource used for the Arduino database jdbc/ArduinoDM_DB - jdbc:h2:repository/database/VirtualFireAlarmDM_DB;DB_CLOSE_ON_EXIT=FALSE + jdbc:h2:repository/database/ArduinoDM_DB;DB_CLOSE_ON_EXIT=FALSE wso2carbon wso2carbon diff --git a/features/device-mgt-iot-feature/org.wso2.carbon.device.mgt.iot.feature/src/main/resources/conf/devicetype-config.xml b/features/device-mgt-iot-feature/org.wso2.carbon.device.mgt.iot.feature/src/main/resources/conf/devicetype-config.xml index 2a74769500..89a16261d1 100644 --- a/features/device-mgt-iot-feature/org.wso2.carbon.device.mgt.iot.feature/src/main/resources/conf/devicetype-config.xml +++ b/features/device-mgt-iot-feature/org.wso2.carbon.device.mgt.iot.feature/src/main/resources/conf/devicetype-config.xml @@ -3,15 +3,18 @@ jdbc/AndroidSenseDM_DB - - jdbc/ArduinoDM_DB - + + + jdbc/DigitalDisplayDM_DB - - jdbc/RaspberryPiDM_DB + + jdbc/DroneAnalyzerDM_DB + + + jdbc/VirtualFireAlarmDM_DB diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/pom.xml b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/pom.xml new file mode 100644 index 0000000000..af9555bd3c --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/pom.xml @@ -0,0 +1,142 @@ + + + + + + + + org.wso2.carbon.devicemgt-plugins + device-mgt-iot-raspberrypi-feature + 1.9.2-SNAPSHOT + ../pom.xml + + + 4.0.0 + org.wso2.carbon.device.mgt.iot.raspberrypi.feature + pom + 1.9.2-SNAPSHOT + WSO2 Carbon - IoT Server RaspberryPi Feature + http://wso2.org + This feature contains the RaspberryPi Device type specific implementations for the IoT Server + + + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl + + + + + + + + + + + + maven-resources-plugin + + + copy-resources + generate-resources + + copy-resources + + + src/main/resources + + + resources + + build.properties + p2.inf + + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-jaxrs-war + package + + copy + + + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.iot.raspberrypi.service.impl + + war + true + ${basedir}/src/main/resources/webapps/ + raspberrypi.war + + + + + + + + + org.wso2.maven + carbon-p2-plugin + + + p2-feature-generation + package + + p2-feature-gen + + + org.wso2.carbon.device.mgt.iot.raspberrypi + ../../../features/etc/feature.properties + + + org.wso2.carbon.p2.category.type:server + org.eclipse.equinox.p2.type.group:false + + + + + org.wso2.carbon.devicemgt-plugins:org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl:${carbon.iot.device.mgt.version} + + + + org.wso2.carbon.core.server:${carbon.kernel.version} + + org.wso2.carbon.device.mgt.server:${carbon.device.mgt.version} + + + + + + + + + diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/agent/raspberrypi.deb b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/agent/raspberrypi.deb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/agent/sketch.properties b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/agent/sketch.properties new file mode 100644 index 0000000000..2341219c4f --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/agent/sketch.properties @@ -0,0 +1,20 @@ +# +# 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. +# + +templates=RaspberryPi.deb +zipfilename=RaspberryPi.zip \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/build.properties b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/build.properties new file mode 100644 index 0000000000..9c86577d76 --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/build.properties @@ -0,0 +1 @@ +custom = true diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/configs/raspberrypi.xml b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/configs/raspberrypi.xml new file mode 100644 index 0000000000..c0865e368a --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/configs/raspberrypi.xml @@ -0,0 +1,24 @@ + + + + + + jdbc/RaspberryPiDM_DB + + diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/datasources/raspberrypi-datasources.xml b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/datasources/raspberrypi-datasources.xml new file mode 100644 index 0000000000..62982ef294 --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/datasources/raspberrypi-datasources.xml @@ -0,0 +1,48 @@ + + + + + org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader + + + + + RaspberryPi_DB + The datasource used for the RaspberryPi database + + jdbc/RaspberryPiDM_DB + + + + jdbc:h2:repository/database/RaspberryPiDM_DB;DB_CLOSE_ON_EXIT=FALSE + + wso2carbon + wso2carbon + org.h2.Driver + 50 + 60000 + true + SELECT 1 + 30000 + + + + + + diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/dbscripts/h2_raspberrypi.sql b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/dbscripts/h2_raspberrypi.sql new file mode 100644 index 0000000000..aead055a48 --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/dbscripts/h2_raspberrypi.sql @@ -0,0 +1,11 @@ + +-- ----------------------------------------------------- +-- Table `RASPBERRYPI_DEVICE` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `RASPBERRYPI_DEVICE` ( + `RASPBERRYPI_DEVICE_ID` VARCHAR(45) NOT NULL , + `DEVICE_NAME` VARCHAR(100) NULL DEFAULT NULL, + PRIMARY KEY (`RASPBERRYPI_DEVICE_ID`) ); + + + diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/dbscripts/mysql_raspberrypi.sql b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/dbscripts/mysql_raspberrypi.sql new file mode 100644 index 0000000000..ad0ec54b7f --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/dbscripts/mysql_raspberrypi.sql @@ -0,0 +1,12 @@ +-- ----------------------------------------------------- +-- Table `RASPBERRYPI_DEVICE` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `RASPBERRYPI_DEVICE` ( + `RASPBERRYPI_DEVICE_ID` VARCHAR(45) NOT NULL , + `DEVICE_NAME` VARCHAR(100) NULL DEFAULT NULL, + PRIMARY KEY (`RASPBERRYPI_DEVICE_ID`) ) +ENGINE = InnoDB; + + + + diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.hbs b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.hbs new file mode 100644 index 0000000000..23c82c6ba8 --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.hbs @@ -0,0 +1,126 @@ +{{unit "cdmf.unit.lib.service-invoker-utility"}} +{{unit "cdmf.unit.lib.handlebars"}} +{{#defineZone "device-detail-top"}} +
+
+ +
+
+{{/defineZone}} + + +
+ +
+
+
+
+
+
+ +
+
+
+
Device Overview - RaspberryPi
+ {{#defineZone "device-detail-properties"}} + + + + + + {{#if device.viewModel.udid}} + + {{/if}} + + + + + +
Device{{device.viewModel.vendor}} {{device.properties.model}}
Model{{device.viewModel.model}}
IMEI{{device.viewModel.imei}}
UDID{{device.viewModel.udid}}
Status + {{#equal device.status "ACTIVE"}} Active{{/equal}} + {{#equal device.status "INACTIVE"}} Inactive{{/equal}} +
+ {{/defineZone}} +
Operations
+
+ {{unit "iot.unit.device.operation" device=device}} +
+
+
+
+ +
+ + {{#defineZone "device-detail-properties"}} +
+
+ +
+
Device Details
+ {{unit "iot.unit.device.stats" device=device}} +
+ +
+
Policy Compliance
+
+ +
+
+ Not available yet +
+
+
+
+
+
+
Operations Log
+
+ +
+
+ Not available yet +
+
+
+
+
+
+
+ {{/defineZone}} +
+
+
+ +
+ +{{#zone "bottomJs"}} + + + +{{/zone}} \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.js b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.js new file mode 100644 index 0000000000..202e3ac973 --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.js @@ -0,0 +1,43 @@ +/* + * 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. + */ + +function onRequest(context) { + var log = new Log("device-view.js"); + var deviceType = context.uriParams.deviceType; + var deviceId = request.getParameter("id"); + + 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) { + var viewModel = {}; + var deviceInfo = device.properties.DEVICE_INFO; + if (deviceInfo != undefined && String(deviceInfo.toString()).length > 0) { + deviceInfo = parse(stringify(deviceInfo)); + viewModel.system = device.properties.IMEI; + viewModel.machine = "RaspberryPi"; + viewModel.vendor = device.properties.VENDOR; + } + device.viewModel = viewModel; + } + context.device = device; + + return context; + } +} \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.json b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.json new file mode 100644 index 0000000000..9eecd8f5bf --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/device-view.json @@ -0,0 +1,3 @@ +{ + "version": "1.0.0" +} \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/images/thumb.png b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/images/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..3a15c499d38bb4608c4b4ef054cbdb24e848d28d GIT binary patch literal 62544 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 0){ + var content = template(viewModel); + if(!update) { + $("#operations-log-container").html(content); + operationTable = $('#operations-log-table').datatables_extended(); + }else{ + $('#operations-log-table').dataTable().fnClearTable(); + for(var i=0; i < data.length; i++) { + var status; + if(data[i].status == "COMPLETED") { + status = " Completed"; + } else if(data[i].status == "PENDING") { + status = " Pending"; + } else if(data[i].status == "ERROR") { + status = " Error"; + } else if(data[i].status == "IN_PROGRESS") { + status = " In Progress"; + } + + $('#operations-log-table').dataTable().fnAddData([ + data[i].code, + status, + data[i].createdTimeStamp + ]); + } + } + } + + }; + invokerUtil.get(serviceURL, + successCallback, function(message){ + console.log(message); + }); + }); + + } + + function loadApplicationsList() { + var applicationsList = $("#applications-list"); + var deviceListingSrc = applicationsList.attr("src"); + var deviceId = applicationsList.data("device-id"); + var deviceType = applicationsList.data("device-type"); + + $.template("application-list", deviceListingSrc, function (template) { + var serviceURL = "/devicemgt_admin/operations/"+deviceType+"/"+deviceId+"/apps"; + + var successCallback = function (data) { + data = JSON.parse(data); + $('#apps-spinner').addClass('hidden'); + var viewModel = {}; + if(data != null && data.length > 0) { + for (var i = 0; i < data.length; i++) { + data[i].name = data[i].name.replace(/[^\w\s]/gi, ' '); + data[i].name = data[i].name.replace(/[0-9]/g, ' '); + } + } + viewModel.applications = data; + viewModel.deviceType = deviceType; + if(data.length > 0){ + var content = template(viewModel); + $("#applications-list-container").html(content); + } + + }; + invokerUtil.get(serviceURL, + successCallback, function(message){ + console.log(message); + }); + }); + } + + function loadPolicyCompliance() { + var policyCompliance = $("#policy-view"); + var policySrc = policyCompliance.attr("src"); + var deviceId = policyCompliance.data("device-id"); + var deviceType = policyCompliance.data("device-type"); + var activePolicy = null; + + $.template("policy-view", policySrc, function (template) { + var serviceURLPolicy ="/devicemgt_admin/policies/"+deviceType+"/"+deviceId+"/active-policy" + var serviceURLCompliance = "/devicemgt_admin/policies/"+deviceType+"/"+deviceId; + + var successCallbackCompliance = function (data) { + var viewModel = {}; + viewModel.policy = activePolicy; + viewModel.deviceType = deviceType; + if(data != null && data.complianceFeatures!= null && data.complianceFeatures != undefined && data.complianceFeatures.length > 0) { + viewModel.compliance = "NON-COMPLIANT"; + viewModel.complianceFeatures = data.complianceFeatures; + var content = template(viewModel); + $("#policy-list-container").html(content); + } else { + viewModel.compliance = "COMPLIANT"; + var content = template(viewModel); + $("#policy-list-container").html(content); + $("#policy-compliance-table").addClass("hidden"); + } + + }; + + var successCallbackPolicy = function (data) { + data = JSON.parse(data); + $('#policy-spinner').addClass('hidden'); + if(data != null && data.active == true){ + activePolicy = data; + invokerUtil.get(serviceURLCompliance, + successCallbackCompliance, function(message){ + console.log(message); + }); + } + }; + + invokerUtil.get(serviceURLPolicy, + successCallbackPolicy, function(message){ + console.log(message); + }); + }); + + } + +}()); diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/js/load-map.js b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/js/load-map.js new file mode 100644 index 0000000000..92ae2d2d8d --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/js/load-map.js @@ -0,0 +1,55 @@ +/* + * 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. + */ + +$(document).ready(function(){ + if (document.getElementById('device-location')){ + loadMap(); + } +}); + +function loadMap() { + var map; + function initialize() { + var mapOptions = { + zoom: 18 + }; + var lat = $("#device-location").data("lat"); + var long = $("#device-location").data("long"); + + if(lat != null && lat != undefined && lat != "" && long != null && long != undefined && long != "") { + $("#map-error").hide(); + $("#device-location").show(); + map = new google.maps.Map(document.getElementById('device-location'), + mapOptions); + + var pos = new google.maps.LatLng(lat, + long); + var marker = new google.maps.Marker({ + position: pos, + map: map + }); + + map.setCenter(pos); + }else{ + $("#device-location").hide(); + $("#map-error").show(); + } + + } + google.maps.event.addDomListener(window, 'load', initialize); +} \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/applications-list.hbs b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/applications-list.hbs new file mode 100644 index 0000000000..8fba28621b --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/applications-list.hbs @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/operations-log.hbs b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/operations-log.hbs new file mode 100644 index 0000000000..cc5db5117a --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/operations-log.hbs @@ -0,0 +1,24 @@ + + + + + + + + + + {{#each operations}} + + + + + + {{/each}} +
+ +
Operation CodeStatusRequest created at
{{code}} + {{#equal status "COMPLETED"}} Completed{{/equal}} + {{#equal status "PENDING"}} Pending{{/equal}} + {{#equal status "ERROR"}} Error{{/equal}} + {{#equal status "IN_PROGRESS"}} In Progress{{/equal}} + {{createdTimeStamp}}
\ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/policy-compliance.hbs b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/policy-compliance.hbs new file mode 100644 index 0000000000..6dabda7870 --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.device-view/public/templates/policy-compliance.hbs @@ -0,0 +1,79 @@ +
+ +
+
+ + {{#equal deviceType "android"}} + + {{/equal}} + {{#equal deviceType "ios"}} + + {{/equal}} + {{#equal deviceType "windows"}} + + {{/equal}} + + +

{{policy.policyName}}

+ {{deviceType}} +
+
+
+
+
+
+ Ownership Type : {{policy.ownershipType}} +
+
+
+
+ Compliance Type : {{policy.compliance}} +
+
+
+
+ Compliance : + {{#equal compliance "COMPLIANT"}} + Compliant + {{/equal}} + {{#equal compliance "NON-COMPLIANT"}} + Not Compliant + {{/equal}} +
+
+
+
+ +
+
+
+ + + + + + + + + {{#each complianceFeatures}} + + + + + {{/each}} +
+ +
FeatureCompliance
{{featureCode}} + {{#equal compliance true}} Compliant{{/equal}} + {{#equal compliance false}} Not Compliant{{/equal}} +
\ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/private/conf/device-type.json b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/private/conf/device-type.json new file mode 100644 index 0000000000..d7662e6a6d --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/private/conf/device-type.json @@ -0,0 +1,8 @@ +{ + "deviceType": { + "label": "RaspberryPi", + "category": "iot", + "downloadAgentUri": "https://localhost:9443/raspberrypi/RaspberryPiDeviceManager/manager/device/raspberrypi/download", + "downloadAgentLinkGenUri" : "https://localhost:9443/raspberrypi/RaspberryPiDeviceManager/manager/device/raspberrypi/generate_link" + } +} \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/public/images/thunb.png b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/public/images/thunb.png new file mode 100644 index 0000000000000000000000000000000000000000..eb0e28cb26fd12e71a6645a8f35be731e2e268a1 GIT binary patch literal 8006 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 4) { + payload.deviceName = deviceName; + invokerUtil.post( + downloadDeviceAPI, + payload, + function (data, textStatus, jqxhr) { + doAction(data); + }, + function (data) { + doAction(data); + } + ); + }else if(deviceName){ + $('.controls').append(''); + $('.control-group').removeClass('success').addClass('error'); + } else { + $('.controls').append(''); + $('.control-group').removeClass('success').addClass('error'); + } + }); + + $("a#download-device-cancel-link").click(function () { + hidePopup(); + }); + + }); +} + +function downloadAgent() { + $('#downloadForm').submit(); + + var deviceName; + $('.new-device-name').each(function () { + if (this.value != "") { + deviceName = this.value; + } + }); + if (deviceName && deviceName.length >= 4) { + setTimeout(function () { + hidePopup(); + }, 1000); + } +} + +function doAction(data) { + //if it is saml redirection response + if (data.status == null) { + document.write(data); + } + + if (data.status == "200") { + $(modalPopupContent).html($('#download-device-modal-content-links').html()); + $("input#download-device-url").val(data.responseText); + $("input#download-device-url").focus(function () { + $(this).select(); + }); + showPopup(); + } else if (data.status == "401") { + $(modalPopupContent).html($('#device-401-content').html()); + $("#device-401-link").click(function () { + window.location = "/devicemgt/login"; + }); + showPopup(); + } else if (data == "403") { + $(modalPopupContent).html($('#device-403-content').html()); + $("#device-403-link").click(function () { + window.location = "/devicemgt/login"; + }); + showPopup(); + } else { + $(modalPopupContent).html($('#device-unexpected-error-content').html()); + $("a#device-unexpected-error-link").click(function () { + hidePopup(); + }); + } +} \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/public/js/jquery.validate.js b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/public/js/jquery.validate.js new file mode 100644 index 0000000000..3c1ebb043b --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/public/js/jquery.validate.js @@ -0,0 +1,1220 @@ +/** + * jQuery Validation Plugin 1.11.0pre + * + * http://bassistance.de/jquery-plugins/jquery-plugin-validation/ + * http://docs.jquery.com/Plugins/Validation + * + * Copyright 2013 Jörn Zaefferer + * Released under the MIT license: + * http://www.opensource.org/licenses/mit-license.php + */ + +(function($) { + +$.extend($.fn, { + // http://docs.jquery.com/Plugins/Validation/validate + validate: function( options ) { + + // if nothing is selected, return nothing; can't chain anyway + if ( !this.length ) { + if ( options && options.debug && window.console ) { + console.warn( "Nothing selected, can't validate, returning nothing." ); + } + return; + } + + // check if a validator for this form was already created + var validator = $.data( this[0], "validator" ); + if ( validator ) { + return validator; + } + + // Add novalidate tag if HTML5. + this.attr( "novalidate", "novalidate" ); + + validator = new $.validator( options, this[0] ); + $.data( this[0], "validator", validator ); + + if ( validator.settings.onsubmit ) { + + this.validateDelegate( ":submit", "click", function( event ) { + if ( validator.settings.submitHandler ) { + validator.submitButton = event.target; + } + // allow suppressing validation by adding a cancel class to the submit button + if ( $(event.target).hasClass("cancel") ) { + validator.cancelSubmit = true; + } + }); + + // validate the form on submit + this.submit( function( event ) { + if ( validator.settings.debug ) { + // prevent form submit to be able to see console output + event.preventDefault(); + } + function handle() { + var hidden; + if ( validator.settings.submitHandler ) { + if ( validator.submitButton ) { + // insert a hidden input as a replacement for the missing submit button + hidden = $("").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm); + } + validator.settings.submitHandler.call( validator, validator.currentForm, event ); + if ( validator.submitButton ) { + // and clean up afterwards; thanks to no-block-scope, hidden can be referenced + hidden.remove(); + } + return false; + } + return true; + } + + // prevent submit for invalid forms or custom submit handlers + if ( validator.cancelSubmit ) { + validator.cancelSubmit = false; + return handle(); + } + if ( validator.form() ) { + if ( validator.pendingRequest ) { + validator.formSubmitted = true; + return false; + } + return handle(); + } else { + validator.focusInvalid(); + return false; + } + }); + } + + return validator; + }, + // http://docs.jquery.com/Plugins/Validation/valid + valid: function() { + if ( $(this[0]).is("form")) { + return this.validate().form(); + } else { + var valid = true; + var validator = $(this[0].form).validate(); + this.each(function() { + valid &= validator.element(this); + }); + return valid; + } + }, + // attributes: space seperated list of attributes to retrieve and remove + removeAttrs: function( attributes ) { + var result = {}, + $element = this; + $.each(attributes.split(/\s/), function( index, value ) { + result[value] = $element.attr(value); + $element.removeAttr(value); + }); + return result; + }, + // http://docs.jquery.com/Plugins/Validation/rules + rules: function( command, argument ) { + var element = this[0]; + + if ( command ) { + var settings = $.data(element.form, "validator").settings; + var staticRules = settings.rules; + var existingRules = $.validator.staticRules(element); + switch(command) { + case "add": + $.extend(existingRules, $.validator.normalizeRule(argument)); + staticRules[element.name] = existingRules; + if ( argument.messages ) { + settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages ); + } + break; + case "remove": + if ( !argument ) { + delete staticRules[element.name]; + return existingRules; + } + var filtered = {}; + $.each(argument.split(/\s/), function( index, method ) { + filtered[method] = existingRules[method]; + delete existingRules[method]; + }); + return filtered; + } + } + + var data = $.validator.normalizeRules( + $.extend( + {}, + $.validator.classRules(element), + $.validator.attributeRules(element), + $.validator.dataRules(element), + $.validator.staticRules(element) + ), element); + + // make sure required is at front + if ( data.required ) { + var param = data.required; + delete data.required; + data = $.extend({required: param}, data); + } + + return data; + } +}); + +// Custom selectors +$.extend($.expr[":"], { + // http://docs.jquery.com/Plugins/Validation/blank + blank: function( a ) { return !$.trim("" + a.value); }, + // http://docs.jquery.com/Plugins/Validation/filled + filled: function( a ) { return !!$.trim("" + a.value); }, + // http://docs.jquery.com/Plugins/Validation/unchecked + unchecked: function( a ) { return !a.checked; } +}); + +// constructor for validator +$.validator = function( options, form ) { + this.settings = $.extend( true, {}, $.validator.defaults, options ); + this.currentForm = form; + this.init(); +}; + +$.validator.format = function( source, params ) { + if ( arguments.length === 1 ) { + return function() { + var args = $.makeArray(arguments); + args.unshift(source); + return $.validator.format.apply( this, args ); + }; + } + if ( arguments.length > 2 && params.constructor !== Array ) { + params = $.makeArray(arguments).slice(1); + } + if ( params.constructor !== Array ) { + params = [ params ]; + } + $.each(params, function( i, n ) { + source = source.replace( new RegExp("\\{" + i + "\\}", "g"), function() { + return n; + }); + }); + return source; +}; + +$.extend($.validator, { + + defaults: { + messages: {}, + groups: {}, + rules: {}, + errorClass: "error", + validClass: "valid", + errorElement: "label", + focusInvalid: true, + errorContainer: $([]), + errorLabelContainer: $([]), + onsubmit: true, + ignore: ":hidden", + ignoreTitle: false, + onfocusin: function( element, event ) { + this.lastActive = element; + + // hide error label and remove error class on focus if enabled + if ( this.settings.focusCleanup && !this.blockFocusCleanup ) { + if ( this.settings.unhighlight ) { + this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass ); + } + this.addWrapper(this.errorsFor(element)).hide(); + } + }, + onfocusout: function( element, event ) { + if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) { + this.element(element); + } + }, + onkeyup: function( element, event ) { + if ( event.which === 9 && this.elementValue(element) === "" ) { + return; + } else if ( element.name in this.submitted || element === this.lastElement ) { + this.element(element); + } + }, + onclick: function( element, event ) { + // click on selects, radiobuttons and checkboxes + if ( element.name in this.submitted ) { + this.element(element); + } + // or option elements, check parent select in that case + else if ( element.parentNode.name in this.submitted ) { + this.element(element.parentNode); + } + }, + highlight: function( element, errorClass, validClass ) { + if ( element.type === "radio" ) { + this.findByName(element.name).addClass(errorClass).removeClass(validClass); + } else { + $(element).addClass(errorClass).removeClass(validClass); + } + }, + unhighlight: function( element, errorClass, validClass ) { + if ( element.type === "radio" ) { + this.findByName(element.name).removeClass(errorClass).addClass(validClass); + } else { + $(element).removeClass(errorClass).addClass(validClass); + } + } + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults + setDefaults: function( settings ) { + $.extend( $.validator.defaults, settings ); + }, + + messages: { + required: "This field is required.", + remote: "Please fix this field.", + email: "Please enter a valid email address.", + url: "Please enter a valid URL.", + date: "Please enter a valid date.", + dateISO: "Please enter a valid date (ISO).", + number: "Please enter a valid number.", + digits: "Please enter only digits.", + creditcard: "Please enter a valid credit card number.", + equalTo: "Please enter the same value again.", + maxlength: $.validator.format("Please enter no more than {0} characters."), + minlength: $.validator.format("Please enter at least {0} characters."), + rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."), + range: $.validator.format("Please enter a value between {0} and {1}."), + max: $.validator.format("Please enter a value less than or equal to {0}."), + min: $.validator.format("Please enter a value greater than or equal to {0}.") + }, + + autoCreateRanges: false, + + prototype: { + + init: function() { + this.labelContainer = $(this.settings.errorLabelContainer); + this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm); + this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer ); + this.submitted = {}; + this.valueCache = {}; + this.pendingRequest = 0; + this.pending = {}; + this.invalid = {}; + this.reset(); + + var groups = (this.groups = {}); + $.each(this.settings.groups, function( key, value ) { + if ( typeof value === "string" ) { + value = value.split(/\s/); + } + $.each(value, function( index, name ) { + groups[name] = key; + }); + }); + var rules = this.settings.rules; + $.each(rules, function( key, value ) { + rules[key] = $.validator.normalizeRule(value); + }); + + function delegate(event) { + var validator = $.data(this[0].form, "validator"), + eventType = "on" + event.type.replace(/^validate/, ""); + if ( validator.settings[eventType] ) { + validator.settings[eventType].call(validator, this[0], event); + } + } + $(this.currentForm) + .validateDelegate(":text, [type='password'], [type='file'], select, textarea, " + + "[type='number'], [type='search'] ,[type='tel'], [type='url'], " + + "[type='email'], [type='datetime'], [type='date'], [type='month'], " + + "[type='week'], [type='time'], [type='datetime-local'], " + + "[type='range'], [type='color'] ", + "focusin focusout keyup", delegate) + .validateDelegate("[type='radio'], [type='checkbox'], select, option", "click", delegate); + + if ( this.settings.invalidHandler ) { + $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler); + } + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/form + form: function() { + this.checkForm(); + $.extend(this.submitted, this.errorMap); + this.invalid = $.extend({}, this.errorMap); + if ( !this.valid() ) { + $(this.currentForm).triggerHandler("invalid-form", [this]); + } + this.showErrors(); + return this.valid(); + }, + + checkForm: function() { + this.prepareForm(); + for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) { + this.check( elements[i] ); + } + return this.valid(); + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/element + element: function( element ) { + element = this.validationTargetFor( this.clean( element ) ); + this.lastElement = element; + this.prepareElement( element ); + this.currentElements = $(element); + var result = this.check( element ) !== false; + if ( result ) { + delete this.invalid[element.name]; + } else { + this.invalid[element.name] = true; + } + if ( !this.numberOfInvalids() ) { + // Hide error containers on last error + this.toHide = this.toHide.add( this.containers ); + } + this.showErrors(); + return result; + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/showErrors + showErrors: function( errors ) { + if ( errors ) { + // add items to error list and map + $.extend( this.errorMap, errors ); + this.errorList = []; + for ( var name in errors ) { + this.errorList.push({ + message: errors[name], + element: this.findByName(name)[0] + }); + } + // remove items from success list + this.successList = $.grep( this.successList, function( element ) { + return !(element.name in errors); + }); + } + if ( this.settings.showErrors ) { + this.settings.showErrors.call( this, this.errorMap, this.errorList ); + } else { + this.defaultShowErrors(); + } + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/resetForm + resetForm: function() { + if ( $.fn.resetForm ) { + $(this.currentForm).resetForm(); + } + this.submitted = {}; + this.lastElement = null; + this.prepareForm(); + this.hideErrors(); + this.elements().removeClass( this.settings.errorClass ).removeData( "previousValue" ); + }, + + numberOfInvalids: function() { + return this.objectLength(this.invalid); + }, + + objectLength: function( obj ) { + var count = 0; + for ( var i in obj ) { + count++; + } + return count; + }, + + hideErrors: function() { + this.addWrapper( this.toHide ).hide(); + }, + + valid: function() { + return this.size() === 0; + }, + + size: function() { + return this.errorList.length; + }, + + focusInvalid: function() { + if ( this.settings.focusInvalid ) { + try { + $(this.findLastActive() || this.errorList.length && this.errorList[0].element || []) + .filter(":visible") + .focus() + // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find + .trigger("focusin"); + } catch(e) { + // ignore IE throwing errors when focusing hidden elements + } + } + }, + + findLastActive: function() { + var lastActive = this.lastActive; + return lastActive && $.grep(this.errorList, function( n ) { + return n.element.name === lastActive.name; + }).length === 1 && lastActive; + }, + + elements: function() { + var validator = this, + rulesCache = {}; + + // select all valid inputs inside the form (no submit or reset buttons) + return $(this.currentForm) + .find("input, select, textarea") + .not(":submit, :reset, :image, [disabled]") + .not( this.settings.ignore ) + .filter(function() { + if ( !this.name ) { + if ( window.console ) { + console.error( "%o has no name assigned", this ); + } + throw new Error( "Failed to validate, found an element with no name assigned. See console for element reference." ); + } + + // select only the first element for each name, and only those with rules specified + if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) { + return false; + } + + rulesCache[this.name] = true; + return true; + }); + }, + + clean: function( selector ) { + return $(selector)[0]; + }, + + errors: function() { + var errorClass = this.settings.errorClass.replace(" ", "."); + return $(this.settings.errorElement + "." + errorClass, this.errorContext); + }, + + reset: function() { + this.successList = []; + this.errorList = []; + this.errorMap = {}; + this.toShow = $([]); + this.toHide = $([]); + this.currentElements = $([]); + }, + + prepareForm: function() { + this.reset(); + this.toHide = this.errors().add( this.containers ); + }, + + prepareElement: function( element ) { + this.reset(); + this.toHide = this.errorsFor(element); + }, + + elementValue: function( element ) { + var type = $(element).attr("type"), + val = $(element).val(); + + if ( type === "radio" || type === "checkbox" ) { + return $("input[name='" + $(element).attr("name") + "']:checked").val(); + } + + if ( typeof val === "string" ) { + return val.replace(/\r/g, ""); + } + return val; + }, + + check: function( element ) { + element = this.validationTargetFor( this.clean( element ) ); + + var rules = $(element).rules(); + var dependencyMismatch = false; + var val = this.elementValue(element); + var result; + + for (var method in rules ) { + var rule = { method: method, parameters: rules[method] }; + try { + + result = $.validator.methods[method].call( this, val, element, rule.parameters ); + + // if a method indicates that the field is optional and therefore valid, + // don't mark it as valid when there are no other rules + if ( result === "dependency-mismatch" ) { + dependencyMismatch = true; + continue; + } + dependencyMismatch = false; + + if ( result === "pending" ) { + this.toHide = this.toHide.not( this.errorsFor(element) ); + return; + } + + if ( !result ) { + this.formatAndAdd( element, rule ); + return false; + } + } catch(e) { + if ( this.settings.debug && window.console ) { + console.log( "Exception occured when checking element " + element.id + ", check the '" + rule.method + "' method.", e ); + } + throw e; + } + } + if ( dependencyMismatch ) { + return; + } + if ( this.objectLength(rules) ) { + this.successList.push(element); + } + return true; + }, + + // return the custom message for the given element and validation method + // specified in the element's HTML5 data attribute + customDataMessage: function( element, method ) { + return $(element).data("msg-" + method.toLowerCase()) || (element.attributes && $(element).attr("data-msg-" + method.toLowerCase())); + }, + + // return the custom message for the given element name and validation method + customMessage: function( name, method ) { + var m = this.settings.messages[name]; + return m && (m.constructor === String ? m : m[method]); + }, + + // return the first defined argument, allowing empty strings + findDefined: function() { + for(var i = 0; i < arguments.length; i++) { + if ( arguments[i] !== undefined ) { + return arguments[i]; + } + } + return undefined; + }, + + defaultMessage: function( element, method ) { + return this.findDefined( + this.customMessage( element.name, method ), + this.customDataMessage( element, method ), + // title is never undefined, so handle empty string as undefined + !this.settings.ignoreTitle && element.title || undefined, + $.validator.messages[method], + "Warning: No message defined for " + element.name + "" + ); + }, + + formatAndAdd: function( element, rule ) { + var message = this.defaultMessage( element, rule.method ), + theregex = /\$?\{(\d+)\}/g; + if ( typeof message === "function" ) { + message = message.call(this, rule.parameters, element); + } else if (theregex.test(message)) { + message = $.validator.format(message.replace(theregex, "{$1}"), rule.parameters); + } + this.errorList.push({ + message: message, + element: element + }); + + this.errorMap[element.name] = message; + this.submitted[element.name] = message; + }, + + addWrapper: function( toToggle ) { + if ( this.settings.wrapper ) { + toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); + } + return toToggle; + }, + + defaultShowErrors: function() { + var i, elements; + for ( i = 0; this.errorList[i]; i++ ) { + var error = this.errorList[i]; + if ( this.settings.highlight ) { + this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); + } + this.showLabel( error.element, error.message ); + } + if ( this.errorList.length ) { + this.toShow = this.toShow.add( this.containers ); + } + if ( this.settings.success ) { + for ( i = 0; this.successList[i]; i++ ) { + this.showLabel( this.successList[i] ); + } + } + if ( this.settings.unhighlight ) { + for ( i = 0, elements = this.validElements(); elements[i]; i++ ) { + this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass ); + } + } + this.toHide = this.toHide.not( this.toShow ); + this.hideErrors(); + this.addWrapper( this.toShow ).show(); + }, + + validElements: function() { + return this.currentElements.not(this.invalidElements()); + }, + + invalidElements: function() { + return $(this.errorList).map(function() { + return this.element; + }); + }, + + showLabel: function( element, message ) { + var label = this.errorsFor( element ); + if ( label.length ) { + // refresh error/success class + label.removeClass( this.settings.validClass ).addClass( this.settings.errorClass ); + + // check if we have a generated label, replace the message then + if ( label.attr("generated") ) { + label.html(message); + } + } else { + // create label + label = $("<" + this.settings.errorElement + "/>") + .attr({"for": this.idOrName(element), generated: true}) + .addClass(this.settings.errorClass) + .html(message || ""); + if ( this.settings.wrapper ) { + // make sure the element is visible, even in IE + // actually showing the wrapped element is handled elsewhere + label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent(); + } + if ( !this.labelContainer.append(label).length ) { + if ( this.settings.errorPlacement ) { + this.settings.errorPlacement(label, $(element) ); + } else { + label.insertAfter(element); + } + } + } + if ( !message && this.settings.success ) { + label.text(""); + if ( typeof this.settings.success === "string" ) { + label.addClass( this.settings.success ); + } else { + this.settings.success( label, element ); + } + } + this.toShow = this.toShow.add(label); + }, + + errorsFor: function( element ) { + var name = this.idOrName(element); + return this.errors().filter(function() { + return $(this).attr("for") === name; + }); + }, + + idOrName: function( element ) { + return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name); + }, + + validationTargetFor: function( element ) { + // if radio/checkbox, validate first element in group instead + if ( this.checkable(element) ) { + element = this.findByName( element.name ).not(this.settings.ignore)[0]; + } + return element; + }, + + checkable: function( element ) { + return (/radio|checkbox/i).test(element.type); + }, + + findByName: function( name ) { + return $(this.currentForm).find("[name='" + name + "']"); + }, + + getLength: function( value, element ) { + switch( element.nodeName.toLowerCase() ) { + case "select": + return $("option:selected", element).length; + case "input": + if ( this.checkable( element) ) { + return this.findByName(element.name).filter(":checked").length; + } + } + return value.length; + }, + + depend: function( param, element ) { + return this.dependTypes[typeof param] ? this.dependTypes[typeof param](param, element) : true; + }, + + dependTypes: { + "boolean": function( param, element ) { + return param; + }, + "string": function( param, element ) { + return !!$(param, element.form).length; + }, + "function": function( param, element ) { + return param(element); + } + }, + + optional: function( element ) { + var val = this.elementValue(element); + return !$.validator.methods.required.call(this, val, element) && "dependency-mismatch"; + }, + + startRequest: function( element ) { + if ( !this.pending[element.name] ) { + this.pendingRequest++; + this.pending[element.name] = true; + } + }, + + stopRequest: function( element, valid ) { + this.pendingRequest--; + // sometimes synchronization fails, make sure pendingRequest is never < 0 + if ( this.pendingRequest < 0 ) { + this.pendingRequest = 0; + } + delete this.pending[element.name]; + if ( valid && this.pendingRequest === 0 && this.formSubmitted && this.form() ) { + $(this.currentForm).submit(); + this.formSubmitted = false; + } else if (!valid && this.pendingRequest === 0 && this.formSubmitted) { + $(this.currentForm).triggerHandler("invalid-form", [this]); + this.formSubmitted = false; + } + }, + + previousValue: function( element ) { + return $.data(element, "previousValue") || $.data(element, "previousValue", { + old: null, + valid: true, + message: this.defaultMessage( element, "remote" ) + }); + } + + }, + + classRuleSettings: { + required: {required: true}, + email: {email: true}, + url: {url: true}, + date: {date: true}, + dateISO: {dateISO: true}, + number: {number: true}, + digits: {digits: true}, + creditcard: {creditcard: true} + }, + + addClassRules: function( className, rules ) { + if ( className.constructor === String ) { + this.classRuleSettings[className] = rules; + } else { + $.extend(this.classRuleSettings, className); + } + }, + + classRules: function( element ) { + var rules = {}; + var classes = $(element).attr("class"); + if ( classes ) { + $.each(classes.split(" "), function() { + if ( this in $.validator.classRuleSettings ) { + $.extend(rules, $.validator.classRuleSettings[this]); + } + }); + } + return rules; + }, + + attributeRules: function( element ) { + var rules = {}; + var $element = $(element); + + for (var method in $.validator.methods) { + var value; + + // support for in both html5 and older browsers + if ( method === "required" ) { + value = $element.get(0).getAttribute(method); + // Some browsers return an empty string for the required attribute + // and non-HTML5 browsers might have required="" markup + if ( value === "" ) { + value = true; + } + // force non-HTML5 browsers to return bool + value = !!value; + } else { + value = $element.attr(method); + } + + if ( value ) { + rules[method] = value; + } else if ( $element[0].getAttribute("type") === method ) { + rules[method] = true; + } + } + + // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs + if ( rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength) ) { + delete rules.maxlength; + } + + return rules; + }, + + dataRules: function( element ) { + var method, value, + rules = {}, $element = $(element); + for (method in $.validator.methods) { + value = $element.data("rule-" + method.toLowerCase()); + if ( value !== undefined ) { + rules[method] = value; + } + } + return rules; + }, + + staticRules: function( element ) { + var rules = {}; + var validator = $.data(element.form, "validator"); + if ( validator.settings.rules ) { + rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {}; + } + return rules; + }, + + normalizeRules: function( rules, element ) { + // handle dependency check + $.each(rules, function( prop, val ) { + // ignore rule when param is explicitly false, eg. required:false + if ( val === false ) { + delete rules[prop]; + return; + } + if ( val.param || val.depends ) { + var keepRule = true; + switch (typeof val.depends) { + case "string": + keepRule = !!$(val.depends, element.form).length; + break; + case "function": + keepRule = val.depends.call(element, element); + break; + } + if ( keepRule ) { + rules[prop] = val.param !== undefined ? val.param : true; + } else { + delete rules[prop]; + } + } + }); + + // evaluate parameters + $.each(rules, function( rule, parameter ) { + rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter; + }); + + // clean number parameters + $.each(["minlength", "maxlength", "min", "max"], function() { + if ( rules[this] ) { + rules[this] = Number(rules[this]); + } + }); + $.each(["rangelength", "range"], function() { + var parts; + if ( rules[this] ) { + if ( $.isArray(rules[this]) ) { + rules[this] = [Number(rules[this][0]), Number(rules[this][1])]; + } else if ( typeof rules[this] === "string" ) { + parts = rules[this].split(/[\s,]+/); + rules[this] = [Number(parts[0]), Number(parts[1])]; + } + } + }); + + if ( $.validator.autoCreateRanges ) { + // auto-create ranges + if ( rules.min && rules.max ) { + rules.range = [rules.min, rules.max]; + delete rules.min; + delete rules.max; + } + if ( rules.minlength && rules.maxlength ) { + rules.rangelength = [rules.minlength, rules.maxlength]; + delete rules.minlength; + delete rules.maxlength; + } + } + + return rules; + }, + + // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} + normalizeRule: function( data ) { + if ( typeof data === "string" ) { + var transformed = {}; + $.each(data.split(/\s/), function() { + transformed[this] = true; + }); + data = transformed; + } + return data; + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/addMethod + addMethod: function( name, method, message ) { + $.validator.methods[name] = method; + $.validator.messages[name] = message !== undefined ? message : $.validator.messages[name]; + if ( method.length < 3 ) { + $.validator.addClassRules(name, $.validator.normalizeRule(name)); + } + }, + + methods: { + + // http://docs.jquery.com/Plugins/Validation/Methods/required + required: function( value, element, param ) { + // check if dependency is met + if ( !this.depend(param, element) ) { + return "dependency-mismatch"; + } + if ( element.nodeName.toLowerCase() === "select" ) { + // could be an array for select-multiple or a string, both are fine this way + var val = $(element).val(); + return val && val.length > 0; + } + if ( this.checkable(element) ) { + return this.getLength(value, element) > 0; + } + return $.trim(value).length > 0; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/remote + remote: function( value, element, param ) { + if ( this.optional(element) ) { + return "dependency-mismatch"; + } + + var previous = this.previousValue(element); + if (!this.settings.messages[element.name] ) { + this.settings.messages[element.name] = {}; + } + previous.originalMessage = this.settings.messages[element.name].remote; + this.settings.messages[element.name].remote = previous.message; + + param = typeof param === "string" && {url:param} || param; + + if ( previous.old === value ) { + return previous.valid; + } + + previous.old = value; + var validator = this; + this.startRequest(element); + var data = {}; + data[element.name] = value; + $.ajax($.extend(true, { + url: param, + mode: "abort", + port: "validate" + element.name, + dataType: "json", + data: data, + success: function( response ) { + validator.settings.messages[element.name].remote = previous.originalMessage; + var valid = response === true || response === "true"; + if ( valid ) { + var submitted = validator.formSubmitted; + validator.prepareElement(element); + validator.formSubmitted = submitted; + validator.successList.push(element); + delete validator.invalid[element.name]; + validator.showErrors(); + } else { + var errors = {}; + var message = response || validator.defaultMessage( element, "remote" ); + errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message; + validator.invalid[element.name] = true; + validator.showErrors(errors); + } + previous.valid = valid; + validator.stopRequest(element, valid); + } + }, param)); + return "pending"; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/minlength + minlength: function( value, element, param ) { + var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element); + return this.optional(element) || length >= param; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/maxlength + maxlength: function( value, element, param ) { + var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element); + return this.optional(element) || length <= param; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/rangelength + rangelength: function( value, element, param ) { + var length = $.isArray( value ) ? value.length : this.getLength($.trim(value), element); + return this.optional(element) || ( length >= param[0] && length <= param[1] ); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/min + min: function( value, element, param ) { + return this.optional(element) || value >= param; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/max + max: function( value, element, param ) { + return this.optional(element) || value <= param; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/range + range: function( value, element, param ) { + return this.optional(element) || ( value >= param[0] && value <= param[1] ); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/email + email: function( value, element ) { + // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/ + return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/url + url: function( value, element ) { + // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/ + return this.optional(element) || /^(https?|s?ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/date + date: function( value, element ) { + return this.optional(element) || !/Invalid|NaN/.test(new Date(value).toString()); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/dateISO + dateISO: function( value, element ) { + return this.optional(element) || /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}$/.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/number + number: function( value, element ) { + return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/digits + digits: function( value, element ) { + return this.optional(element) || /^\d+$/.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/creditcard + // based on http://en.wikipedia.org/wiki/Luhn + creditcard: function( value, element ) { + if ( this.optional(element) ) { + return "dependency-mismatch"; + } + // accept only spaces, digits and dashes + if ( /[^0-9 \-]+/.test(value) ) { + return false; + } + var nCheck = 0, + nDigit = 0, + bEven = false; + + value = value.replace(/\D/g, ""); + + for (var n = value.length - 1; n >= 0; n--) { + var cDigit = value.charAt(n); + nDigit = parseInt(cDigit, 10); + if ( bEven ) { + if ( (nDigit *= 2) > 9 ) { + nDigit -= 9; + } + } + nCheck += nDigit; + bEven = !bEven; + } + + return (nCheck % 10) === 0; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/equalTo + equalTo: function( value, element, param ) { + // bind to the blur event of the target in order to revalidate whenever the target field is updated + // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead + var target = $(param); + if ( this.settings.onfocusout ) { + target.unbind(".validate-equalTo").bind("blur.validate-equalTo", function() { + $(element).valid(); + }); + } + return value === target.val(); + } + + } + +}); + +// deprecated, use $.validator.format instead +$.format = $.validator.format; + +}(jQuery)); + +// ajax mode: abort +// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); +// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() +(function($) { + var pendingRequests = {}; + // Use a prefilter if available (1.5+) + if ( $.ajaxPrefilter ) { + $.ajaxPrefilter(function( settings, _, xhr ) { + var port = settings.port; + if ( settings.mode === "abort" ) { + if ( pendingRequests[port] ) { + pendingRequests[port].abort(); + } + pendingRequests[port] = xhr; + } + }); + } else { + // Proxy ajax + var ajax = $.ajax; + $.ajax = function( settings ) { + var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode, + port = ( "port" in settings ? settings : $.ajaxSettings ).port; + if ( mode === "abort" ) { + if ( pendingRequests[port] ) { + pendingRequests[port].abort(); + } + return (pendingRequests[port] = ajax.apply(this, arguments)); + } + return ajax.apply(this, arguments); + }; + } +}(jQuery)); + +// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation +// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target +(function($) { + $.extend($.fn, { + validateDelegate: function( delegate, type, handler ) { + return this.bind(type, function( event ) { + var target = $(event.target); + if ( target.is(delegate) ) { + return handler.apply(target, arguments); + } + }); + } + }); +}(jQuery)); diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/type-view.hbs b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/type-view.hbs new file mode 100644 index 0000000000..d2c006fafe --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/type-view.hbs @@ -0,0 +1,242 @@ +
+
+

RaspberryPi

+
+

Connect your RaspberryPi device to the WSO2 IoT Server.

+
+
+ +
+ +
+

Ingredients

+
+

Hardware Requirements

+
+
    +
  • + Raspberry Pi (Internet Enabled) +
  • +
  • + DHT11 Temperature Sensor +
  • +
  • + LED +
  • +
  • + Buzzer(3v) +
  • +
+
+ + View API
  + Download + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +

+
+ +
+

Prepare

+
+

Get your device ready

+
+
    +
  • 01 Get your RaspberryPi device and download the Agent + Sketch. +
  • +
  • 02 Open the downloaded RaspberryPi sketch and fill in + the pins that you wish to read/control. +
  • +
  • 03 Burn the sketch onto your RaspberryPi board and + let the program run. +
  • +
+
+
+
+ +{{#zone "topCss"}} + +{{/zone}} + +{{#zone "bottomJs"}} + {{js "/js/download.js"}} + {{js "/js/jquery.validate.js"}} + +{{/zone}} \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/type-view.json b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/type-view.json new file mode 100644 index 0000000000..9eecd8f5bf --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.raspberrypi.type-view/type-view.json @@ -0,0 +1,3 @@ +{ + "version": "1.0.0" +} \ No newline at end of file diff --git a/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/p2.inf b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/p2.inf new file mode 100644 index 0000000000..ee3719f970 --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/org.wso2.carbon.device.mgt.iot.raspberrypi.feature/src/main/resources/p2.inf @@ -0,0 +1,13 @@ +instructions.configure = \ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../conf/devicetype-conf/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.raspberrypi_${feature.version}/configs/,target:${installFolder}/../../conf/devicetype-conf/,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/webapps/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.raspberrypi_${feature.version}/webapps/,target:${installFolder}/../../deployment/server/webapps/,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../resources/sketches/);\ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../resources/sketches/raspberrypi/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.raspberrypi_${feature.version}/agent/,target:${installFolder}/../../resources/sketches/raspberrypi/,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.raspberrypi_${feature.version}/dbscripts/,target:${installFolder}/../../../dbscripts/cdm/plugins/raspberrypi,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/jaggeryapps/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.raspberrypi_${feature.version}/jaggeryapps/,target:${installFolder}/../../deployment/server/jaggeryapps/,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../conf/datasources/devicemgt/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.iot.raspberrypi_${feature.version}/datasources/,target:${installFolder}/../../conf/datasources/devicemgt,overwrite:true);\ diff --git a/features/device-mgt-iot-raspberrypi-feature/pom.xml b/features/device-mgt-iot-raspberrypi-feature/pom.xml new file mode 100644 index 0000000000..57229cb214 --- /dev/null +++ b/features/device-mgt-iot-raspberrypi-feature/pom.xml @@ -0,0 +1,41 @@ + + + + + + + org.wso2.carbon.devicemgt-plugins + carbon-device-mgt-plugins-parent + 1.9.2-SNAPSHOT + ../../pom.xml + + + 4.0.0 + device-mgt-iot-raspberrypi-feature + 1.9.2-SNAPSHOT + pom + WSO2 Carbon - IoT Server RaspberryPi Device Feature + http://wso2.org + + + org.wso2.carbon.device.mgt.iot.raspberrypi.feature + + + diff --git a/pom.xml b/pom.xml index f02e7a3d11..49ccfd54dd 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ components/device-mgt-iot-arduino components/device-mgt-iot-digitaldisplay components/device-mgt-iot-droneanalyzer + components/device-mgt-iot-raspberrypi components/device-mgt-iot-virtualfirealarm @@ -66,6 +67,7 @@ features/device-mgt-iot-arduino-feature features/device-mgt-iot-digitaldisplay-feature features/device-mgt-iot-droneanalyzer-feature + features/device-mgt-iot-raspberrypi-feature features/device-mgt-iot-virtualfirealarm-feature @@ -423,6 +425,20 @@ war + + + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.iot.raspberrypi.plugin.impl + ${carbon.iot.device.mgt.version} + + + + + + +