public class ZWayConstants {
    public static final String PATH_LOGIN = "login";
    public static final String PATH_STATUS = "status";
    public static final String PATH_NAMESPACES = "namespaces";
    public static final String PATH_MODULES = "modules";
    public static final String PATH_INSTANCES = "instances";
    public static final String PATH_DEVICES = "devices";
    public static final String PATH_DEVICES_COMMAND = "devices/{deviceId}/command/{command}";
    public static final String PATH_LOCATIONS = "locations";
    public static final String PATH_PROFILES = "profiles";
    public static final String PATH_NOTIFICATIONS = "notifications";
    public static final String PATH_DEVICE_HISTORY = "history";
    public static final String PATH_ICONS = "icons";
    public static final String PATH_SYSTEM_INFO = "system/info";

    public static final String REMOTE_PATH_LOGIN = "zboxweb";

    public static final String ZWAVE_PATH_DEVICES = "devices[{nodeId}]";
    public static final String ZWAVE_PATH_CONTROLLER = "controller";
    public static final String ZWAVE_PATH_INCLUSION = "controller.AddNodeToNetwork({flag})";
    public static final String ZWAVE_PATH_EXCLUSION = "controller.RemoveNodeFromNetwork({flag})";
    public static final String ZWAVE_PATH_CONTROLLER_DATA = "controller.data";

    public static final String ZWAVE_PATH_DEVICES_CC_THERMOSTAT_SET = "devices[{nodeId}].ThermostatMode.Set({mode})";

    public static final String ICON_NONE = "";
    public static final String ICON_SWITCH = "switch";
    public static final String ICON_MULTILEVEL = "multilevel";
    public static final String ICON_SMOKE = "smoke";
    public static final String ICON_CO = "co";
    public static final String ICON_COOLING = "cooling";
    public static final String ICON_DOOR = "door";
    public static final String ICON_MOTION = "motion";
    public static final String ICON_FLOOD = "flood";
    public static final String ICON_TEMPERATURE = "temperature";
    public static final String ICON_LUMINOSITY = "luminosity";
    public static final String ICON_ENERGY = "energy";
    public static final String ICON_HUMIDITY = "humidity";
    public static final String ICON_BATTERY = "battery";
    public static final String ICON_THERMOSTAT = "thermostat";
    public static final String ICON_FAN = "fan";
    public static final String ICON_METER = "meter";
    public static final String ICON_BLINDS = "blinds";
    public static final String ICON_TAMPER = "tamper";
    public static final String ICON_BAROMETER = "barometer";
    public static final String ICON_ULTRAVIOLET = "ultraviolet";
    public static final String ICON_ALARM = "alarm";
    public static final String ICON_GESTURE = "gesture";

    public static final String PROBE_TYPE_TEMPERATURE = "temperature";
    public static final String PROBE_TYPE_LUMINOSITY = "luminosity";
    public static final String PROBE_TYPE_HUMIDITY = "humidity";
    public static final String PROBE_TYPE_ULTRAVIOLET = "ultraviolet";
    public static final String PROBE_TYPE_GENERAL_PURPOSE = "general_purpose";
    public static final String PROBE_TYPE_ENERGY = "energy";
    public static final String PROBE_TYPE_MOTOR = "motor";
    public static final String PROBE_TYPE_MULTILEVEL = "multilevel";
    public static final String PROBE_TYPE_SMOKE = "smoke";
    public static final String PROBE_TYPE_CO = "co";
    public static final String PROBE_TYPE_FLOOD = "flood";
    public static final String PROBE_TYPE_COOLING = "cooling";
    public static final String PROBE_TYPE_TAMPER = "tamper";
    public static final String PROBE_TYPE_DOOR_WINDOW = "door-window";
    public static final String PROBE_TYPE_MOTION = "motion";
    public static final String PROBE_TYPE_BAROMETER = "barometer";

    public static final String PROBE_TYPE_METER_ELECTRIC_KILOWATT_PER_HOUR = "meterElectric_kilowatt_per_hour";
    public static final String PROBE_TYPE_METER_ELECTRIC_WATT = "meterElectric_watt";
    public static final String PROBE_TYPE_METER_ELECTRIC_PULSE_COUNT = "meterElectric_pulse_count";
    public static final String PROBE_TYPE_METER_ELECTRIC_VOLTAGE = "meterElectric_voltage";
    public static final String PROBE_TYPE_METER_ELECTRIC_AMPERE = "meterElectric_ampere";
    public static final String PROBE_TYPE_METER_ELECTRIC_POWER_FACTOR = "meterElectric_power_factor";

    public static final String PROBE_TYPE_BATTERY = "Battery"; // attention! uppercase

    public static final String PROBE_TYPE_ALARM_DOOR = "alarm_door";
    public static final String PROBE_TYPE_ALARM_SMOKE = "alarm_smoke";
    public static final String PROBE_TYPE_ALARM_CO = "alarm_co";
    public static final String PROBE_TYPE_ALARM_COO = "alarm_coo";
    public static final String PROBE_TYPE_ALARM_HEAT = "alarm_heat";
    public static final String PROBE_TYPE_ALARM_FLOOD = "alarm_flood";
    public static final String PROBE_TYPE_ALARM_BURGLAR = "alarm_burglar";
    public static final String PROBE_TYPE_ALARM_POWER = "alarm_power";
    public static final String PROBE_TYPE_ALARM_SYSTEM = "alarm_system";
    public static final String PROBE_TYPE_ALARM_EMERGENCY = "alarm_emergency";
    public static final String PROBE_TYPE_ALARM_CLOCK = "alarm_clock";

    // probe types for deprecated command class AlarmSensor

    public static final String PROBE_TYPE_ALARM_SENSOR_GENERAL_PURPOSE = "alarmSensor_door";
    public static final String PROBE_TYPE_ALARM_SENSOR_SMOKE = "alarmSensor_smoke";
    public static final String PROBE_TYPE_ALARM_SENSOR_CO = "alarmSensor_co";
    public static final String PROBE_TYPE_ALARM_SENSOR_COO = "alarmSensor_coo";
    public static final String PROBE_TYPE_ALARM_SENSOR_HEAT = "alarmSensor_heat";
    public static final String PROBE_TYPE_ALARM_SENSOR_FLOOD = "alarmSensor_flood";
    public static final String PROBE_TYPE_ALARM_SENSOR_DOOR = "alarmSensor_door";
    public static final String PROBE_TYPE_ALARM_SENSOR_BURGLAR = "alarmSensor_burglar";
    public static final String PROBE_TYPE_ALARM_SENSOR_POWER = "alarmSensor_power";
    public static final String PROBE_TYPE_ALARM_SENSOR_SYSTEM = "alarmSensor_system";
    public static final String PROBE_TYPE_ALARM_SENSOR_EMERGENCY = "alarmSensor_emergency";
    public static final String PROBE_TYPE_ALARM_SENSOR_CLOCK = "alarmSensor_clock";

    // end - probe types for deprecated command class AlarmSensor

    public static final String PROBE_TYPE_SWITCH_COLOR_RGB = "switchColor_rgb";
    public static final String PROBE_TYPE_SWITCH_COLOR_SOFT_WHITE = "switchColor_soft_white";
    public static final String PROBE_TYPE_SWITCH_COLOR_COLD_WHITE = "switchColor_cold_white";
    public static final String PROBE_TYPE_SWITCH_COLOR_RED = "switchColor_red";
    public static final String PROBE_TYPE_SWITCH_COLOR_GREEN = "switchColor_green";
    public static final String PROBE_TYPE_SWITCH_COLOR_BLUE = "switchColor_blue";

    public static final String PROBE_TYPE_THERMOSTAT_MODE = "thermostat_mode";
    public static final String PROBE_TYPE_THERMOSTAT_SET_POINT = "thermostat_set_point";

    public static final String PROBE_TITLE_BATTERY = "Battery";
    public static final String PROBE_TITLE_GENERAL_PURPOSE = "General purpose";
    public static final String PROBE_TITLE_TEMPERATURE = "Temperature";
    public static final String PROBE_TITLE_LUMINISCENCE = "Luminiscence";
    public static final String PROBE_TITLE_HUMIDITY = "Humidity";
    public static final String PROBE_TITLE_ULTRAVIOLET = "Ultraviolet";
    public static final String PROBE_TITLE_POWER = "Power";
    public static final String PROBE_TITLE_ELECTRIC = "Electric"; // attention! scaleTitles: W, kWh
    public static final String PROBE_TITLE_CO2_LEVEL = "CO2 Level";
    public static final String PROBE_TITLE_CONTROL = "Control";

    public static final String DEVICE_TYPE_BATTERY = "battery";
    public static final String DEVICE_TYPE_DOORLOCK = "doorlock";
    public static final String DEVICE_TYPE_THERMOSTAT = "thermostat";
    public static final String DEVICE_TYPE_SWITCH_BINARY = "switchBinary";
    public static final String DEVICE_TYPE_SWITCH_MULTILEVEL = "switchMultilevel";
    public static final String DEVICE_TYPE_SENSOR_BINARY = "sensorBinary";
    public static final String DEVICE_TYPE_SENSOR_MULTILEVEL = "sensorMultilevel";
    public static final String DEVICE_TYPE_SWITCH_TOGGLE = "switchToggle";
    public static final String DEVICE_TYPE_SWITCH_CONTROL = "switchControl";
    public static final String DEVICE_TYPE_TOGGLE_BUTTON = "toggleButton";
    public static final String DEVICE_TYPE_SWITCH_RGBW = "switchRGBW";
    public static final String DEVICE_TYPE_TEXT = "text";
    public static final String DEVICE_TYPE_CAMERA = "camera";
    public static final String DEVICE_TYPE_SENSOR_MULTILINE = "sensorMultiline";
    public static final String DEVICE_TYPE_SENSOR_DISCRETE = "sensorDiscrete";

    public static final String NOTIFICATION_TYPE_DEVICE_INFO = "device-info";
} public class DeviceHistoryData {
    @SerializedName("id")
    private Long mId;
    @SerializedName("l")
    private String mLevel;

    public DeviceHistoryData() {
        mId = -1l;
        mLevel = "";
    }

    public Long getId() {
        if (mId == null) {
            mId = -1l;
        }
        return mId;
    }

    public void setId(Long id) {
        this.mId = id;
    }

    public String getLevel() {
        if (mLevel == null) {
            mLevel = "";
        }
        return mLevel;
    }

    public void setLevel(String level) {
        this.mLevel = level;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("id", mId).append("level", mLevel).toString();
    }
} public class DeviceHistoryList {
    private List<DeviceHistory> mDeviceHistoryList;

    /**
     * Instantiate a device history list with default values.
     */
    public DeviceHistoryList() {
        mDeviceHistoryList = new ArrayList<DeviceHistory>();
    }

    public void addDeviceHistory(DeviceHistory deviceHistory) {
        mDeviceHistoryList.add(deviceHistory);
    }

    public List<DeviceHistory> getDeviceHistoryList() {
        return mDeviceHistoryList;
    }

    /**
     * Return the first occurrence of device history
     *
     * @param id ZAutomation device id
     * @return device history or null if not found
     */
    public DeviceHistory getDeviceHistoryByDeviceId(String deviceId) {
        for (DeviceHistory deviceHistory : mDeviceHistoryList) {
            if (deviceHistory.getDeviceId().equals(deviceId)) {
                return deviceHistory;
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);
        for (DeviceHistory deviceHistory : mDeviceHistoryList) {
            toStringBuilder.append(deviceHistory);
        }

        return new ToStringBuilder(this).append("deviceHistoryList", toStringBuilder.toString()).toString();
    }
} public class DeviceHistoryListDeserializer {
    /**
     * @param deviceHistoriesAsJson device history list as JSON data
     * @return device history list
     */
    public DeviceHistoryList deserializeDeviceHistoryList(JsonArray deviceHistoriesAsJson) {
        DeviceHistoryList deviceHistoryList = new DeviceHistoryList();

        for (JsonElement deviceHistoryAsJson : deviceHistoriesAsJson) {
            DeviceHistory deviceHistory = deserializeDeviceHistory(deviceHistoryAsJson);

            if (deviceHistory != null) {
                deviceHistoryList.addDeviceHistory(deviceHistory);
            }
        }

        return deviceHistoryList;
    }

    /**
     * @param deviceHistoryAsJson a single device history as JSON data
     * @return deviceHistory
     */
    public DeviceHistory deserializeDeviceHistory(JsonElement deviceHistoryAsJson) {
        Gson gson = new Gson();

        DeviceHistory deviceHistory = gson.fromJson(deviceHistoryAsJson, DeviceHistory.class);

        return deviceHistory;
    }
} public abstract class Device {
    private transient Logger logger = LoggerFactory.getLogger(this.getClass());

    @SerializedName("creationTime")
    private Integer mCreationTime;
    @SerializedName("creatorId")
    private Integer mCreatorId;
    @SerializedName("deviceType")
    private String mDeviceType;
    @SerializedName("h")
    private Integer mH;
    @SerializedName("hasHistory")
    private Boolean mHasHistory;
    @SerializedName("id")
    private String mDeviceId;
    @SerializedName("location")
    private Integer mLocation;
    @SerializedName("permanently_hidden")
    private Boolean mPermanentlyHidden;
    @SerializedName("probeType")
    private String mProbeType;
    @SerializedName("visibility")
    private Boolean mVisibility;
    @SerializedName("updateTime")
    private Integer mUpdateTime;

    @SerializedName("metrics")
    private Metrics mMetrics;
    @SerializedName("tags")
    private List<String> mTags;

    @SerializedName("customIcons")
    private Icons mCustomIcons;

    // Transient exclude field from serialization!
    protected transient IDeviceCommands mCommandHandler;

    /**
     * Instantiate a device with default values.
     */
    public Device() {
        mCreationTime = -1;
        mCreatorId = -1;
        mDeviceType = "";
        mH = -1;
        mHasHistory = false;
        mDeviceId = "";
        mLocation = -1;
        mPermanentlyHidden = false;
        mProbeType = "";
        mVisibility = false;
        mUpdateTime = -1;

        mMetrics = new Metrics();
        mTags = new ArrayList<String>();

        mCustomIcons = new Icons();
    }

    public Integer getCreationTime() {
        if (mCreationTime == null) {
            mCreationTime = -1;
        }
        return mCreationTime;
    }

    public void setCreationTime(Integer creationTime) {
        this.mCreationTime = creationTime;
    }

    public Integer getCreatorId() {
        if (mCreatorId == null) {
            mCreatorId = -1;
        }
        return mCreatorId;
    }

    public void setCreatorId(Integer creatorId) {
        this.mCreatorId = creatorId;
    }

    public String getDeviceType() {
        if (mDeviceType == null) {
            mDeviceType = "";
        }
        return mDeviceType;
    }

    public void setDeviceType(String deviceType) {
        this.mDeviceType = deviceType;
    }

    public Integer getH() {
        if (mH == null) {
            mH = -1;
        }
        return mH;
    }

    public void setH(Integer h) {
        this.mH = h;
    }

    public Boolean getHasHistory() {
        if (mHasHistory == null) {
            mHasHistory = false;
        }
        return mHasHistory;
    }

    public void setHasHistory(Boolean hasHistory) {
        this.mHasHistory = hasHistory;
    }

    public String getDeviceId() {
        if (mDeviceId == null) {
            mDeviceId = "";
        }
        return mDeviceId;
    }

    public void setDeviceId(String deviceId) {
        this.mDeviceId = deviceId;
    }

    public Integer getLocation() {
        if (mLocation == null) {
            mLocation = -1;
        }
        return mLocation;
    }

    public void setLocation(Integer location) {
        this.mLocation = location;
    }

    public Boolean getPermanentlyHidden() {
        if (mPermanentlyHidden == null) {
            mPermanentlyHidden = false;
        }
        return mPermanentlyHidden;
    }

    public void setPermanentlyHidden(Boolean permanently_hidden) {
        this.mPermanentlyHidden = permanently_hidden;
    }

    public String getProbeType() {
        if (mProbeType == null) {
            mProbeType = "";
        }
        return mProbeType;
    }

    public void setProbeType(String probeType) {
        this.mProbeType = probeType;
    }

    public Boolean getVisibility() {
        if (mVisibility == null) {
            mVisibility = false;
        }
        return mVisibility;
    }

    public void setVisibility(Boolean visability) {
        this.mVisibility = visability;
    }

    public Integer getUpdateTime() {
        if (mUpdateTime == null) {
            mUpdateTime = -1;
        }
        return mUpdateTime;
    }

    public void setUpdateTime(Integer updateTime) {
        this.mUpdateTime = updateTime;
    }

    public Metrics getMetrics() {
        if (mMetrics == null) {
            mMetrics = new Metrics();
        }
        return mMetrics;
    }

    public void setMetrics(Metrics metrics) {
        this.mMetrics = metrics;
    }

    public Icons getIcons() {
        if (mCustomIcons == null) {
            mCustomIcons = new Icons();
        }
        return mCustomIcons;
    }

    public void setIcons(Icons icons) {
        this.mCustomIcons = icons;
    }

    /**
     * Extract the node id from virtual device id.
     * Example: ZWayVDev_13-0-49-5 - node id: 13
     *
     * @return node id
     */
    public Integer getNodeId() {
        // Check if virtual device comes with an associated device
        // ZWayVDev_[Node ID]-[Instance ID]-[Command Class ID]-[Scale ID] (see Z-Way Developers Documentation)
        // Example device id: ZWayVDev_13-0-49-5

        String regex = "(.*)_(\\d+)-(\\d)+-(\\d+)(.*)";

        if (getDeviceId().matches(regex)) {
            logger.debug("Device comes with an associated device.");

            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(getDeviceId());
            if (matcher.matches()) {
                try {
                    return Integer.parseInt(matcher.group(2));
                } catch (NumberFormatException e) {
                    logger.warn("Couldn't match a node id (NumberFormatException).");
                }
            } else {
                logger.warn("Couldn't match a node id.");
            }
        } else {
            logger.debug("Device is only a virtual device.");
        }

        return -1;
    }

    public List<String> getTags() {
        return this.mTags;
    }

    public void setTags(List<String> tags) {
        this.mTags = tags;
    }

    /**
     * Set a Z-Way command handler.
     *
     * @param commandHandler
     */
    public void setCommandHandler(IDeviceCommands commandHandler) {
        mCommandHandler = commandHandler;
    }

    // available commands ...
    public String on() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String off() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String update() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String min() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String max() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String exact(String level) {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String exactSmooth(String level, Integer duration) {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String exact(Integer red, Integer green, Integer blue) {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String up() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String down() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String upMax() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String startUp() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String startDown() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String stop() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String open() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String close() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String increase() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String decrease() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String upstart() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String upstop() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String downstart() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String downstop() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    // Camera (up, down, open, close already defined)

    public String zoomIn() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String zoomOut() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String left() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    public String right() {
        throw new UnsupportedOperationException("Operation not supported by device type " + mDeviceType);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("creationTime", mCreationTime).append("creatorId", mCreatorId)
                .append("deviceType", mDeviceType).append("h", mH).append("hasHistory", mHasHistory)
                .append("deviceId", mDeviceId).append("location", mLocation)
                .append("permanentlyHidden", mPermanentlyHidden).append("probeType", mProbeType)
                .append("visibility", mVisibility).append("updateTime", mUpdateTime).append("metrics", mMetrics)
                .append("tags", mTags).append("customIcons", mCustomIcons).toString();
    }
} public class DeviceCommand {
    private String mDeviceId;
    private String mCommand;
    private Map<String, String> mParams;

    /**
     * Instantiate a command.
     *
     * @param deviceId ZAutomation device id
     * @param command ZAutamation command name
     * @param params parameter map
     */
    public DeviceCommand(String deviceId, String command, Map<String, String> params) {
        setDeviceId(deviceId);
        setCommand(command);
        mParams = params;
    }

    /**
     * Instantiate a command without parameter.
     *
     * @param deviceId ZAutomation device id
     * @param command ZAutamation command name
     */
    public DeviceCommand(String deviceId, String command) {
        setDeviceId(deviceId);
        setCommand(command);
        mParams = null;
    }

    public String getDeviceId() {
        return mDeviceId;
    }

    public void setDeviceId(String deviceId) {
        if (deviceId == null || deviceId.isEmpty()) {
            throw new IllegalArgumentException("device id is required");
        }

        mDeviceId = deviceId;
    }

    public String getCommand() {
        return mCommand;
    }

    public void setCommand(String command) {
        if (command == null || command.isEmpty()) {
            throw new IllegalArgumentException("command is required");
        }

        mCommand = command;
    }

    public Map<String, String> getParams() {
        return mParams;
    }

    public void setParams(Map<String, String> params) {
        mParams = params;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);
        if (mParams != null) {
            for (Entry<String, String> entry : mParams.entrySet()) {
                toStringBuilder.append(entry.getKey(), entry.getValue());
            }
        }

        return new ToStringBuilder(this).append("deviceId", mDeviceId).append("command", mCommand)
                .append("params", toStringBuilder.toString()).toString();
    }
} public class DeviceList {
    private List<Device> mDevices;

    public DeviceList() {
        mDevices = new ArrayList<Device>();
    }

    public void addDevice(Device device) {
        mDevices.add(device);
    }

    /**
     * @return list of virtual devices
     */
    public List<Device> getDevices() {
        ArrayList<Device> virtualDevices = new ArrayList<Device>();

        for (Device device : mDevices) {
            Integer nodeId = device.getNodeId();
            if (nodeId == -1) {
                virtualDevices.add(device);
            }
        }

        return virtualDevices;
    }

    public List<Device> getAllDevices() {
        return mDevices;
    }

    /**
     * Return the first occurrence of device
     *
     * @param deviceId ZAutomation device id
     * @return virtual device
     */
    public Device getDeviceById(String deviceId) {

        for (Device device : mDevices) {
            if (device.getDeviceId().equals(deviceId)) {
                return device;
            }
        }
        return null;
    }

    /**
     * @return list of physical devices with associated virtual devices
     */
    public Map<Integer, List<Device>> getDevicesGroupByNodeId() {
        Map<Integer, List<Device>> physicalDevices = new HashMap<Integer, List<Device>>();

        for (Device device : mDevices) {
            Integer nodeId = device.getNodeId();
            if (nodeId != -1) {
                if (physicalDevices.containsKey(nodeId)) {
                    physicalDevices.get(nodeId).add(device);
                } else {
                    ArrayList<Device> devices = new ArrayList<Device>();
                    devices.add(device);

                    physicalDevices.put(nodeId, devices);
                }
            }
        }

        return physicalDevices;
    }

    /**
     * @param nodeId Z-Wave device node id
     * @return list of physical devices with associated virtual devices
     */
    public Map<Integer, List<Device>> getDevicesByNodeId(Integer nodeId) {
        Map<Integer, List<Device>> physicalDevice = new HashMap<Integer, List<Device>>();

        for (Device device : mDevices) {
            if (nodeId == device.getNodeId()) {
                if (physicalDevice.containsKey(nodeId)) {
                    physicalDevice.get(nodeId).add(device);
                } else {
                    ArrayList<Device> devices = new ArrayList<Device>();
                    devices.add(device);

                    physicalDevice.put(nodeId, devices);
                }
            }
        }

        return physicalDevice;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);
        for (Device device : mDevices) {
            toStringBuilder.append(device);
        }

        return new ToStringBuilder(this).append("deviceList", toStringBuilder.toString()).toString();
    }
} public class DeviceListDeserializer {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * @param devicesAsJson list list as JSON data
     * @param commandHandler Z-Way command handler
     * @return device list
     */
    public DeviceList deserializeDeviceList(JsonArray devicesAsJson, IDeviceCommands commandHandler) {
        DeviceList deviceList = new DeviceList();

        for (JsonElement deviceAsJson : devicesAsJson) {
            Device device = deserializeDevice(deviceAsJson, commandHandler);

            if (device != null) {
                deviceList.addDevice(device);
            }
        }

        return deviceList;
    }

    /**
     * @param deviceAsJson a single device as JSON data
     * @return device
     */
    public Device deserializeDevice(JsonElement deviceAsJson, IDeviceCommands commandHandler) {
        Gson gson = new Gson();

        if (deviceAsJson.getAsJsonObject().has("deviceType")) {
            String deviceType = deviceAsJson.getAsJsonObject().get("deviceType").getAsString();
            Device device = null;
            try {
                switch (deviceType) {
                    case ZWayConstants.DEVICE_TYPE_BATTERY:
                        device = gson.fromJson(deviceAsJson, Battery.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_DOORLOCK:
                        device = gson.fromJson(deviceAsJson, Doorlock.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_THERMOSTAT:
                        device = gson.fromJson(deviceAsJson, Thermostat.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SWITCH_BINARY:
                        device = gson.fromJson(deviceAsJson, SwitchBinary.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SWITCH_MULTILEVEL:
                        device = gson.fromJson(deviceAsJson, SwitchMultilevel.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SENSOR_BINARY:
                        device = gson.fromJson(deviceAsJson, SensorBinary.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SENSOR_MULTILEVEL:
                        device = gson.fromJson(deviceAsJson, SensorMultilevel.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SWITCH_TOGGLE:
                        device = gson.fromJson(deviceAsJson, SwitchToggle.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SWITCH_RGBW:
                        device = gson.fromJson(deviceAsJson, SwitchRGBW.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SWITCH_CONTROL:
                        device = gson.fromJson(deviceAsJson, SwitchControl.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_TOGGLE_BUTTON:
                        device = gson.fromJson(deviceAsJson, ToggleButton.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_TEXT:
                        device = gson.fromJson(deviceAsJson, Text.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_CAMERA:
                        device = gson.fromJson(deviceAsJson, Camera.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SENSOR_DISCRETE:
                        device = gson.fromJson(deviceAsJson, SensorDiscrete.class);
                        break;
                    case ZWayConstants.DEVICE_TYPE_SENSOR_MULTILINE:
                        device = gson.fromJson(deviceAsJson, SensorMultiline.class);
                        break;
                    default:
                        logger.debug("Unknown device type: " + deviceType);
                        break;
                }

                if (device != null) {
                    device.setCommandHandler(commandHandler);
                }
            } catch (Exception e) {
                // Prevent to invalidate whole device list if only one device fails.
                logger.debug("Deserialize device fails: " + e.getMessage());
                device = null;
            }

            return device;
        } else {
            logger.debug("Device type not specified!");
            return null;
        }
    }
} public class IconList {
    private List<Icon> mIcons;

    /**
     * Instantiate a icon list with default values.
     */
    public IconList() {
        mIcons = new ArrayList<Icon>();
    }

    public void addIcon(Icon icon) {
        mIcons.add(icon);
    }

    public List<Icon> getIcons() {
        return mIcons;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);
        for (Icon icon : mIcons) {
            toStringBuilder.append(icon);
        }

        return new ToStringBuilder(this).append("iconList", toStringBuilder.toString()).toString();
    }
} public class IconListDeserializer {
    /**
     * @param iconsAsJson icon list as JSON data
     * @return icon list
     */
    public IconList deserializeIconList(JsonArray iconsAsJson) {
        IconList iconList = new IconList();

        for (JsonElement iconAsJson : iconsAsJson) {
            Icon icon = deserializeIcon(iconAsJson);

            if (icon != null) {
                iconList.addIcon(icon);
            }
        }

        return iconList;
    }

    /**
     * @param iconAsJson a single icon as JSON data
     * @return icon
     */
    public Icon deserializeIcon(JsonElement iconAsJson) {
        Gson gson = new Gson();

        Icon icon = gson.fromJson(iconAsJson, Icon.class);

        return icon;
    }
} public abstract class Instance {
    @SerializedName("instanceId")
    private String mInstanceId;
    @SerializedName("moduleId")
    private String mModuleId;
    @SerializedName("active")
    private Boolean mActive;
    @SerializedName("title")
    private String mTitle;
    @SerializedName("id")
    private Integer mId;
    @SerializedName("creationTime")
    private Long mCreationTime;

    /**
     * Instantiate a instance with default values.
     */
    public Instance() {
        mInstanceId = "";
        mModuleId = "";
        mActive = true;
        mTitle = "";
        mId = -1;
        mCreationTime = new Date().getTime() / 1000;
    }

    public String getInstanceId() {
        if (mInstanceId == null) {
            mInstanceId = "";
        }
        return mInstanceId;
    }

    public void setInstanceId(String instanceId) {
        this.mInstanceId = instanceId;
    }

    public String getModuleId() {
        if (mModuleId == null) {
            mModuleId = "";
        }
        return mModuleId;
    }

    public void setModuleId(String moduleId) {
        this.mModuleId = moduleId;
    }

    public Boolean getActive() {
        if (mActive == null) {
            mActive = true;
        }
        return mActive;
    }

    public void setActive(Boolean active) {
        this.mActive = active;
    }

    public String getTitle() {
        if (mTitle == null) {
            mTitle = "";
        }
        return mTitle;
    }

    public void setTitle(String title) {
        this.mTitle = title;
    }

    public Integer getId() {
        if (mId == null) {
            mId = -1;
        }
        return mId;
    }

    public void setId(Integer id) {
        this.mId = id;
    }

    public Long getCreationTime() {
        if (mCreationTime == null) {
            mCreationTime = -1l;
        }
        return mCreationTime;
    }

    public void setCreationTime(Long creationTime) {
        this.mCreationTime = creationTime;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("instanceId", mInstanceId).append("moduleId", mModuleId)
                .append("active", mActive).append("title", mTitle).append("id", mId)
                .append("creationTime", mCreationTime).toString();
    }
} public class InstanceList {
    private List<Instance> mInstances;

    /**
     * Instantiate a instance list with default values.
     */
    public InstanceList() {
        mInstances = new ArrayList<Instance>();
    }

    public void addInstance(Instance instance) {
        mInstances.add(instance);
    }

    public List<Instance> getInstances() {
        return mInstances;
    }

    /**
     * Return the first occurrence of instance
     *
     * @param moduleId ZAutomation module id
     * @return instance or null if not found
     */
    public Instance getInstanceByModuleId(String moduleId) {
        for (Instance instance : mInstances) {
            if (instance.getModuleId().equals(moduleId)) {
                return instance;
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);
        for (Instance instance : mInstances) {
            toStringBuilder.append(instance);
        }

        return new ToStringBuilder(this).append("instanceList", toStringBuilder.toString()).toString();
    }
} public class InstanceListDeserializer {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * @param instancesAsJson instance list as JSON data
     * @return instance list
     */
    public InstanceList deserializeInstanceList(JsonArray instancesAsJson) {
        InstanceList instanceList = new InstanceList();

        for (JsonElement instanceAsJson : instancesAsJson) {
            Instance instance = deserializeInstance(instanceAsJson);

            if (instance != null) {
                instanceList.addInstance(instance);
            }
        }

        return instanceList;
    }

    /**
     * @param instanceAsJson a single instance as JSON data
     * @return instance
     */
    public Instance deserializeInstance(JsonElement instanceAsJson) {
        Gson gson = new Gson();

        String moduleId = instanceAsJson.getAsJsonObject().get("moduleId").getAsString();
        Instance instance = null;
        switch (moduleId) {
            case "OpenHABConnector":
                instance = gson.fromJson(instanceAsJson, OpenHABConnector.class);
                break;
            case "DummyDevice":
                instance = gson.fromJson(instanceAsJson, DummyDevice.class);
                break;
            default:
                logger.debug("Unknown module id: " + moduleId);
                break;
        }
        return instance;
    }
} public class LocationList {
    private List<Location> mLocations;

    /**
     * Instantiate a location list with default values.
     */
    public LocationList() {
        mLocations = new ArrayList<Location>();
    }

    public void addLocation(Location location) {
        mLocations.add(location);
    }

    public List<Location> getLocations() {
        return mLocations;
    }

    /**
     * Return the first occurrence of location
     *
     * @param id ZAutomation location id
     * @return location or null if not found
     */
    public Location getLocationById(Integer id) {
        for (Location location : mLocations) {
            if (location.getId().equals(id)) {
                return location;
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);
        for (Location location : mLocations) {
            toStringBuilder.append(location);
        }

        return new ToStringBuilder(this).append("locationList", toStringBuilder.toString()).toString();
    }
} public class LocationListDeserializer {
    /**
     * @param locationsAsJson location list as JSON data
     * @return location list
     */
    public LocationList deserializeLocationList(JsonArray locationsAsJson) {
        LocationList locationList = new LocationList();

        for (JsonElement locationAsJson : locationsAsJson) {
            Location location = deserializeLocation(locationAsJson);

            if (location != null) {
                locationList.addLocation(location);
            }
        }

        return locationList;
    }

    /**
     * @param locationAsJson a single location as JSON data
     * @return location
     */
    public Location deserializeLocation(JsonElement locationAsJson) {
        Gson gson = new Gson();

        Location location = gson.fromJson(locationAsJson, Location.class);

        return location;
    }
} public class LoginForm {
    @SerializedName("form")
    private Boolean mForm;
    @SerializedName("login")
    private String mLogin;
    @SerializedName("password")
    private String mPassword;
    @SerializedName("keepme")
    private Boolean mKeepme;
    @SerializedName("default_ui")
    private Integer mDefaultUI;

    /**
     * Instantiate a login form with default values.
     */
    public LoginForm() {
        mForm = true;
        mLogin = "";
        mPassword = "";
        mKeepme = false;
        mDefaultUI = 1;
    }

    /**
     * Instantiate a login form with passed values.
     *
     * @param form should be true
     * @param login username
     * @param password password
     * @param keepme should be false
     * @param defaultUI should be 1
     */
    public LoginForm(Boolean form, String login, String password, Boolean keepme, Integer defaultUI) {
        mForm = form;
        mLogin = login;
        mPassword = password;
        mKeepme = keepme;
        mDefaultUI = defaultUI;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("form", mForm).append("login", mLogin).append("password", mPassword)
                .append("keepme", mKeepme).append("defaultui", mDefaultUI).toString();
    }
} public class NotificationList {
    private List<Notification> mNotifications;

    /**
     * Instantiate a instance list with default values.
     */
    public NotificationList() {
        mNotifications = new ArrayList<Notification>();
    }

    public void addNotification(Notification notification) {
        mNotifications.add(notification);
    }

    public List<Notification> getNotifications() {
        return mNotifications;
    }

    /**
     * /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);
        for (Notification notification : mNotifications) {
            toStringBuilder.append(notification);
        }

        return new ToStringBuilder(this).append("notificationList", toStringBuilder.toString()).toString();
    }
} public class NotificationListDeserializer {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * @param notificationsAsJson notification list as JSON data
     * @return notification list
     */
    public NotificationList deserializeNotificationList(JsonArray notificationsAsJson) {
        NotificationList notificationList = new NotificationList();

        for (JsonElement notificationAsJson : notificationsAsJson) {
            Notification notification = deserializeNotification(notificationAsJson);

            if (notification != null) {
                notificationList.addNotification(notification);
            }
        }

        return notificationList;
    }

    /**
     * @param notificationAsJson a single notification as JSON data
     * @return notification
     */
    public Notification deserializeNotification(JsonElement notificationAsJson) {
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").create();

        String notificationType = notificationAsJson.getAsJsonObject().get("level").getAsString();
        Notification notification = null;
        switch (notificationType) {
            case ZWayConstants.NOTIFICATION_TYPE_DEVICE_INFO:
                notification = gson.fromJson(notificationAsJson, Notification.class);
                break;
            default:
                logger.debug("Unknown notification type: " + notificationType);
        }

        return notification;
    }
} public class Profile {
    @SerializedName("id")
    private Integer mId;
    @SerializedName("name")
    private String mName;
    @SerializedName("lang")
    private String mLang;
    @SerializedName("color")
    private String mColor;
    @SerializedName("dashboard")
    private Set<String> mDashboard;
    @SerializedName("interval")
    private Integer mInterval;
    @SerializedName("rooms")
    private Set<Integer> mRooms;
    @SerializedName("expert_view")
    private Boolean mExpertView;
    @SerializedName("hide_all_device_events")
    private Boolean mHideAllDeviceEvents;
    @SerializedName("hide_system_events")
    private Boolean mHideSystemEvents;
    @SerializedName("hide_single_device_events")
    private Set<String> mHideSingleDeviceEvents;
    @SerializedName("email")
    private String mEmail;

    /**
     * Instantiate a profile with default values.
     */
    public Profile() {
        mId = -1;
        mName = "";
        mLang = "";
        mColor = "";
        mDashboard = new HashSet<>();
        mInterval = -1;
        mRooms = new HashSet<>();
        mExpertView = false;
        mHideAllDeviceEvents = false;
        mHideSystemEvents = false;
        mHideSingleDeviceEvents = new HashSet<>();
        mEmail = "";
    }

    public Integer getId() {
        if (mId == null) {
            mId = -1;
        }
        return mId;
    }

    public void setId(Integer id) {
        this.mId = id;
    }

    public String getName() {
        if (mName == null) {
            mName = "";
        }
        return mName;
    }

    public void setName(String name) {
        this.mName = name;
    }

    public String getLang() {
        if (mLang == null) {
            mLang = "";
        }
        return mLang;
    }

    public void setLang(String lang) {
        this.mLang = lang;
    }

    public String getColor() {
        if (mColor == null) {
            mColor = "";
        }
        return mColor;
    }

    public void setColor(String color) {
        this.mColor = color;
    }

    public Set<String> getDashboard() {
        if (mDashboard == null) {
            mDashboard = new HashSet<>();
        }
        return mDashboard;
    }

    public void setDashboard(Set<String> dashboard) {
        this.mDashboard = dashboard;
    }

    public Integer getInterval() {
        if (mInterval == null) {
            mInterval = -1;
        }
        return mInterval;
    }

    public void setInterval(Integer interval) {
        this.mInterval = interval;
    }

    public Set<Integer> getRooms() {
        if (mRooms == null) {
            mRooms = new HashSet<>();
        }
        return mRooms;
    }

    public void setRooms(Set<Integer> rooms) {
        this.mRooms = rooms;
    }

    public Boolean getExpertView() {
        if (mExpertView == null) {
            mExpertView = false;
        }
        return mExpertView;
    }

    public void setExpertView(Boolean expertView) {
        this.mExpertView = expertView;
    }

    public Boolean getHideAllDeviceEvents() {
        if (mHideAllDeviceEvents == null) {
            mHideAllDeviceEvents = false;
        }
        return mHideAllDeviceEvents;
    }

    public void setHideAllDeviceEvents(Boolean hideAllDeviceEvents) {
        this.mHideAllDeviceEvents = hideAllDeviceEvents;
    }

    public Boolean getHideSystemEvents() {
        if (mHideSystemEvents == null) {
            mHideSystemEvents = false;
        }
        return mHideSystemEvents;
    }

    public void setHideSystemEvents(Boolean hideSystemEvents) {
        this.mHideSystemEvents = hideSystemEvents;
    }

    public Set<String> getHideSingleDeviceEvents() {
        if (mHideSingleDeviceEvents == null) {
            mHideSingleDeviceEvents = new HashSet<>();
        }
        return mHideSingleDeviceEvents;
    }

    public void setHideSingleDeviceEvents(Set<String> hideSingleDeviceEvents) {
        this.mHideSingleDeviceEvents = hideSingleDeviceEvents;
    }

    public String getEmail() {
        if (mEmail == null) {
            mEmail = "";
        }
        return mEmail;
    }

    public void setEmail(String email) {
        this.mEmail = email;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("id", mId).append("name", mName).append("lang", mLang)
                .append("color", mColor).append("dashboard", mDashboard).append("interval", mInterval)
                .append("rooms", mRooms).append("expertView", mExpertView)
                .append("hideAllDeviceEvents", mHideAllDeviceEvents).append("hideSystemEvents", mHideSystemEvents)
                .append("hideSingleDeviceEvents", mHideSingleDeviceEvents).append("email", mEmail).toString();
    }
} public class ProfileList {
    private List<Profile> mProfiles;

    /**
     * Instantiate a instance list with default values.
     */
    public ProfileList() {
        mProfiles = new ArrayList<Profile>();
    }

    public void addProfile(Profile profile) {
        mProfiles.add(profile);
    }

    public List<Profile> getProfiles() {
        return mProfiles;
    }

    /**
     * /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);
        for (Profile profile : mProfiles) {
            toStringBuilder.append(profile);
        }

        return new ToStringBuilder(this).append("profileList", toStringBuilder.toString()).toString();
    }
} public class ProfileListDeserializer {
    /**
     * @param profilesAsJson profile list as JSON data
     * @return profile list
     */
    public ProfileList deserializeProfileList(JsonArray profilesAsJson) {
        ProfileList profileList = new ProfileList();

        for (JsonElement profileAsJson : profilesAsJson) {
            Profile profile = deserializeProfile(profileAsJson);

            if (profile != null) {
                profileList.addProfile(profile);
            }
        }

        return profileList;
    }

    /**
     * @param profileAsJson a single profile as JSON data
     * @return profile
     */
    public Profile deserializeProfile(JsonElement profileAsJson) {
        Gson gson = new Gson();

        Profile profile = gson.fromJson(profileAsJson, Profile.class);

        return profile;
    }
} public class SystemInfo {
    @SerializedName("first_start_up")
    private Date mFirstStartUp;
    @SerializedName("count_of_reconnects")
    private Integer mCountOfReconnects;
    @SerializedName("current_firmware")
    private String mCurrentFirmware;
    @SerializedName("current_firmware_majurity")
    private String mCurrentFirmwareMajurity;

    /**
     * Instantiate a system info element object with default values.
     */

    public SystemInfo() {
        mFirstStartUp = new Date();
        mCountOfReconnects = -1;
        mCurrentFirmware = "";
        mCurrentFirmwareMajurity = "";
    }

    public Date getFirstStartUp() {
        if (mFirstStartUp == null) {
            mFirstStartUp = new Date();
        }
        return mFirstStartUp;
    }

    public void setFirstStartUp(Date firstStartUp) {
        this.mFirstStartUp = firstStartUp;
    }

    public Integer getCountOfReconnects() {
        if (mCountOfReconnects == null) {
            mCountOfReconnects = -1;
        }
        return mCountOfReconnects;
    }

    public void setCountOfReconnects(Integer countOfReconnects) {
        this.mCountOfReconnects = countOfReconnects;
    }

    public String getCurrentFirmware() {
        if (mCurrentFirmware == null) {
            mCurrentFirmware = "";
        }
        return mCurrentFirmware;
    }

    public void setCurrentFirmware(String currentFirmware) {
        this.mCurrentFirmware = currentFirmware;
    }

    public String getCurrentFirmwareMajurity() {
        if (mCurrentFirmwareMajurity == null) {
            mCurrentFirmwareMajurity = "";
        }
        return mCurrentFirmwareMajurity;
    }

    public void setCurrentFirmwareMajurity(String currentFirmwareMajurity) {
        this.mCurrentFirmwareMajurity = currentFirmwareMajurity;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("first_start_up", mFirstStartUp)
                .append("count_of_reconnects", mCountOfReconnects).append("current_firmware", mCurrentFirmware)
                .append("current_firmware_majurity", mCurrentFirmwareMajurity).toString();
    }
} public class ZWaveController {
    @SerializedName("data")
    private ZWaveControllerData mData;

    /**
     * Instantiate a Z-Wave device object with default values.
     */
    public ZWaveController() {
        mData = new ZWaveControllerData();
    }

    public ZWaveControllerData getData() {
        if (mData == null) {
            mData = new ZWaveControllerData();
        }
        return mData;
    }

    public void setData(ZWaveControllerData data) {
        this.mData = data;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("data", mData).toString();
    }
} public class ZWaveDevice {
    @SerializedName("instances")
    private ZWaveDeviceInstances mInstances;
    @SerializedName("data")
    private ZWaveDeviceData mData;
    @SerializedName("id")
    private Integer mId;

    /**
     * Instantiate a Z-Wave device object with default values.
     */
    public ZWaveDevice() {
        mInstances = new ZWaveDeviceInstances();
        mData = new ZWaveDeviceData();
        mId = -1;
    }

    public ZWaveDeviceInstances getInstances() {
        if (mInstances == null) {
            mInstances = new ZWaveDeviceInstances();
        }
        return mInstances;
    }

    public void setInstances(ZWaveDeviceInstances instances) {
        this.mInstances = instances;
    }

    public ZWaveDeviceData getData() {
        if (mData == null) {
            mData = new ZWaveDeviceData();
        }
        return mData;
    }

    public void setData(ZWaveDeviceData data) {
        this.mData = data;
    }

    public Integer getId() {
        if (mId == null) {
            mId = -1;
        }
        return mId;
    }

    public void setId(Integer id) {
        this.mId = id;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("instances", mInstances).append("data", mData).append("id", mId)
                .toString();
    }
}
import static de.fh_zwickau.informatik.sensor.ZWayConstants.*;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Request.FailureListener;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.MultiPartContentProvider;
import org.eclipse.jetty.client.util.PathContentProvider;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistoryData;
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistoryList;
import de.fh_zwickau.informatik.sensor.model.devicehistory.DeviceHistoryListDeserializer;
import de.fh_zwickau.informatik.sensor.model.devices.Device;
import de.fh_zwickau.informatik.sensor.model.devices.DeviceCommand;
import de.fh_zwickau.informatik.sensor.model.devices.DeviceList;
import de.fh_zwickau.informatik.sensor.model.devices.DeviceListDeserializer;
import de.fh_zwickau.informatik.sensor.model.icons.IconList;
import de.fh_zwickau.informatik.sensor.model.icons.IconListDeserializer;
import de.fh_zwickau.informatik.sensor.model.instances.Instance;
import de.fh_zwickau.informatik.sensor.model.instances.InstanceList;
import de.fh_zwickau.informatik.sensor.model.instances.InstanceListDeserializer;
import de.fh_zwickau.informatik.sensor.model.locations.LocationList;
import de.fh_zwickau.informatik.sensor.model.locations.LocationListDeserializer;
import de.fh_zwickau.informatik.sensor.model.login.LoginForm;
import de.fh_zwickau.informatik.sensor.model.notifications.NotificationList;
import de.fh_zwickau.informatik.sensor.model.notifications.NotificationListDeserializer;
import de.fh_zwickau.informatik.sensor.model.profiles.Profile;
import de.fh_zwickau.informatik.sensor.model.profiles.ProfileList;
import de.fh_zwickau.informatik.sensor.model.profiles.ProfileListDeserializer;
import de.fh_zwickau.informatik.sensor.model.system.SystemInfo;
import de.fh_zwickau.informatik.sensor.model.zwaveapi.controller.ZWaveController;
import de.fh_zwickau.informatik.sensor.model.zwaveapi.devices.ZWaveDevice;
/**
 * Copyright (C) 2016 by Software-Systementwicklung Zwickau Research Group
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package de.fh_zwickau.informatik.sensor;






/**
 * The {@link ZWayApiHttp} implements the ZAutomation API. See also:
 * http://docs.zwayhomeautomation.apiary.io/#
 *
 * @author Patrick Hecker - Initial contribution
 */
public class ZWayApiHttp extends ZWayApiBase {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private final static int HTTP_CLIENT_TIMEOUT = 5000;

    private HttpClient mHttpClient = null;

    /**
     * Setup the Z-Way API with passed values.
     *
     * @param ipAddress ip address
     * @param port port number
     * @param protocol protocol
     * @param username username
     * @param password password
     * @param remoteId remote id
     * @param useRemoteService
     * @param caller receive callbacks
     */
    public ZWayApiHttp(String ipAddress, Integer port, String protocol, String username, String password,
            Integer remoteId, Boolean useRemoteService, IZWayApiCallbacks caller) {

        super(ipAddress, port, protocol, username, password, remoteId, useRemoteService, caller);

        initHttpClient();
    }

    /*
     * (non-Javadoc)
     *
     * @see de.fh_zwickau.informatik.sensor.ZWayApiBase#getLogin()
     */
    @Override
    public synchronized String getLogin() {
        try {
            startHttpClient();

            if (mUseRemoteService) {
                mZWaySessionId = null;
                mZWayRemoteSessionId = null;

                // Build request body
                String body = "act=login&login=" + mRemoteId + "/" + mUsername + "&pass=" + mPassword;

                logger.info("Remote login body: {}", body);
                logger.info("Remote path: {}", getTopLevelUrl() + "/" + REMOTE_PATH_LOGIN);

                Request request = mHttpClient.newRequest(getTopLevelUrl() + "/" + REMOTE_PATH_LOGIN)
                        .method(HttpMethod.POST).header(HttpHeader.CONTENT_TYPE, "application/x-www-form-urlencoded")
                        .content(new StringContentProvider(body), "application/x-www-form-urlencoded");

                ContentResponse response = request.send();

                // Check HTTP status code
                int statusCode = response.getStatus();

                if (statusCode != HttpStatus.OK_200) {
                    String reason = response.getReason();
                    mCaller.httpStatusError(statusCode, reason, true);
                    logger.debug("Communication with Z-Way server failed: {} {}", statusCode, reason);
                }

                CookieStore cookieStore = mHttpClient.getCookieStore();
                List<HttpCookie> cookies = cookieStore.get(URI.create("https://find.z-wave.me/"));
                for (HttpCookie cookie : cookies) {
                    if (cookie.getName().equals("ZWAYSession")) {
                        mZWaySessionId = cookie.getValue();
                    } else if (cookie.getName().equals("ZBW_SESSID")) {
                        mZWayRemoteSessionId = cookie.getValue();
                    }
                    logger.info("HTTP cookie: {} - {}", cookie.getName(), cookie.getValue());
                }

                if (mZWayRemoteSessionId != null && mZWaySessionId != null) {
                    mCaller.getLoginResponse(mZWaySessionId);
                    return mZWaySessionId;
                } else {
                    logger.warn("Response doesn't contain required cookies");
                    mCaller.responseFormatError("Response doesn't contain required cookies", true);
                }
            } else {
                // Build request body
                LoginForm loginForm = new LoginForm(true, mUsername, mPassword, false, 1);