Jelajahi Sumber

kakao qa release version

pangs 3 tahun lalu
induk
melakukan
d290e70807

TEMPAT SAMPAH
.DS_Store


+ 1 - 1
src/main/java/com/icontrols/kakao/common/SERVERINFO.java

@@ -1,7 +1,7 @@
 package com.icontrols.kakao.common;
 package com.icontrols.kakao.common;
 
 
 public class SERVERINFO {
 public class SERVERINFO {
-	public static boolean isDev = false;	// beta 앱으로 배포 할때는 무.조.건. true. 아니면 릴리즈 서버와 통신함.
+	public static boolean isDev = true;	// beta 앱으로 배포 할때는 무.조.건. true. 아니면 릴리즈 서버와 통신함.
 	public static String danzi_code = "";
 	public static String danzi_code = "";
 	public static String danzi_info = "";
 	public static String danzi_info = "";
 	public static String application_version = "";
 	public static String application_version = "";

+ 38 - 16
src/main/java/com/icontrols/kakao/common/THINGS_INFO.java

@@ -16,19 +16,23 @@ public final class THINGS_INFO {
 							THINGS_ILGALSWITCH 	= 5,  THINGS_WALLPAD  	= 6,  THINGS_VENTIL 	= 7,  THINGS_NOTICE 	= 8,
 							THINGS_ILGALSWITCH 	= 5,  THINGS_WALLPAD  	= 6,  THINGS_VENTIL 	= 7,  THINGS_NOTICE 	= 8,
 							THINGS_VOTE			= 9,  THINGS_PARKING  	= 10, THINGS_PARCEL 	= 11, THINGS_HEMS 		= 12,
 							THINGS_VOTE			= 9,  THINGS_PARKING  	= 10, THINGS_PARCEL 	= 11, THINGS_HEMS 		= 12,
 							THINGS_CARIN 		= 13, THINGS_ELEVATOR 	= 14, THINGS_VISITOR 	= 15, THINGS_EMERGENCY 	= 16,
 							THINGS_CARIN 		= 13, THINGS_ELEVATOR 	= 14, THINGS_VISITOR 	= 15, THINGS_EMERGENCY 	= 16,
-							THINGS_LIGHT_DIMMER = 17, THINGS_COLOR_LIGHT_DIMMER = 18;
+							THINGS_LIGHT_DIMMER = 17, THINGS_COLOR_LIGHT_DIMMER = 18, THINGS_AIR_MONITOR = 19, THINGS_AIRCON = 20;
 	
 	
 	public enum THINGS_CAPABILITY{
 	public enum THINGS_CAPABILITY{
-		LIVINGLIGHT("livingLight","{\"capabilities\" : [ { \"name\" : \"Power\" } ]}"),
-		LIGHT("light","{\"capabilities\" : [ { \"name\" : \"Power\" } ]}"),
-		LIGHT_DIMMER("dimming","{\"capabilities\" : [ { \"name\" : \"Power\" }, { \"name\" : \"Brightness\" } ]}"),
-		COLOR_LIGHT("colorLight", "{\"capabilities\" : [ { \"name\" : \"Power\" }, { \"name\" : \"ColorMode\"}, { \"name\" : \"ColorTemperature\"} ]}"),
-		COLOR_LIGHT_DIMMER("colorDimming", "{\"capabilities\" : [ { \"name\" : \"Power\" }, { \"name\" : \"Brightness\" }, { \"name\" : \"ColorMode\"}, { \"name\" : \"ColorTemperature\", \"attributes\" : { \"colorTemperature\": { \"min\":3000, \"max\":5700 } } } ]}"),
-		TEMPER("temper", "{\"capabilities\" : [ { \"name\" : \"Power\" }, { \"name\" : \"Temperature\" }, { \"name\" : \"TemperatureSetpoint\" , \"attributes\" : { \"targetSetpoint\": { \"min\":8, \"max\":40 } } } ]}"),
-		VENTIL("ventil","{\"capabilities\" : [ { \"name\" : \"Power\" }, { \"name\" : \"AirFlow\" } ]}"),
+		LIVINGLIGHT("livingLight","{\"capabilities\" : [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } } ]}"),
+		LIGHT("light","{\"capabilities\" : [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } } ]}"),
+		LIGHT_DIMMER("dimming","{\"capabilities\" : [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } }, { \"name\": \"Brightness\", \"attributes\": { \"brightness\": { \"max\": 10, \"min\": 1, \"mutability\": \"rw\" } } } ]}"),
+		COLOR_LIGHT("colorLight", "{\"capabilities\" : [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } }, { \"name\": \"ColorMode\", \"attributes\": { \"colorMode\": { \"writable\": [ \"colorTemperature\" ], \"supported\": [ \"colorTemperature\" ], \"mutability\": \"rw\" } } }, { \"name\": \"ColorTemperature\", \"attributes\": { \"colorTemperature\": { \"max\": 5700, \"min\": 3000, \"mutability\": \"rw\" } } } ]}"),
+		COLOR_LIGHT_DIMMER("colorDimming", "{\"capabilities\" : [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } }, { \"name\": \"Brightness\", \"attributes\": { \"brightness\": { \"max\": 10, \"min\": 1, \"mutability\": \"rw\" } } }, { \"name\": \"ColorMode\", \"attributes\": { \"colorMode\": { \"writable\": [ \"colorTemperature\" ], \"supported\": [ \"colorTemperature\" ], \"mutability\": \"rw\" } } }, { \"name\": \"ColorTemperature\", \"attributes\": { \"colorTemperature\": { \"max\": 5700, \"min\": 3000, \"mutability\": \"rw\" } } } ]}"),
+		TEMPER("temper", "{\"capabilities\" : [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } }, { \"name\": \"Temperature\", \"attributes\": { \"currentTemperature\": { \"mutability\": \"r\", \"max\": 40, \"min\": 8 } } }, { \"name\": \"TemperatureSetpoint\", \"attributes\": { \"targetSetpoint\": { \"min\": 18, \"max\": 30, \"mutability\": \"rw\" } } } ]}"),
+		VENTIL("ventil","{\"capabilities\" : [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } }, { \"name\": \"AirFlow\", \"attributes\": { \"fanSpeed\": { \"writable\": [ \"low\", \"middle\", \"high\" ], \"supported\": [ \"low\", \"middle\", \"high\" ], \"mutability\": \"rw\" } } } ]}"),
 		GAS("gas","{\"capabilities\" : [ { \"name\" : \"Lock\" } ] }"),
 		GAS("gas","{\"capabilities\" : [ { \"name\" : \"Lock\" } ] }"),
-		ELEVATOR("elevator", "{\"capabilities\" : [ { \"name\" : \"ElevatorCall\" } ] }"),
-		ELECTRIC("electric", "{\"capabilities\" : [ { \"name\" : \"Power\" } ]}");
+		ELEVATOR("elevator", "{ \"capabilities\" : [ { \"name\":\"ElevatorCall\", \"attributes\":{ \"callDirection\":{ \"mutability\":\"rw\", \"writable\":[\"down\"], \"supported\":[\"down\"] } } } ] }"),
+		ELECTRIC("electric", "{\"capabilities\" : [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } } ]}"),
+		PARKING("parcel", "{\"capabilities\" : [ { \"name\" : \"Parking\" , \"attributes\" : { \"parkingLocations\": { \"mutability\":\"r\" } } } ]}"),
+		AIRSENSOR("airsensor", "{ \"capabilities\": [ { \"name\": \"AirQuality\", \"attributes\": { \"co2\": { \"max\": 65534, \"min\": 0, \"mutability\": \"r\" }, \"pm10\": { \"max\": 65534, \"min\": 0, \"mutability\": \"r\" }, \"pm2_5\": { \"max\": 65534, \"min\": 0, \"mutability\": \"r\" }, \"qualityIndex\": { \"supported\": [ \"good\", \"moderate\", \"unhealthy\", \"very_unhealthy\" ], \"mutability\": \"r\" } } } ]}"),
+		PARCEL("parcel", "{\"capabilities\": [ { \"name\": \"Parcel\", \"attributes\": { \"parcelLocations\": { \"mutability\": \"r\" } } } ] }"),
+		AIRCON("aircon", "{ \"capabilities\": [ { \"name\": \"Power\", \"attributes\": { \"power\": { \"writable\": [ \"on\", \"off\" ], \"supported\": [ \"on\", \"off\" ], \"mutability\": \"rw\" } } }, { \"name\": \"AirConditioner\", \"attributes\": { \"mode\": { \"writable\": [ \"cool\", \"dehumidify\", \"fan_only\" ], \"supported\": [ \"cool\", \"dehumidify\", \"fan_only\" ], \"mutability\": \"rw\" } } }, { \"name\": \"AirFlow\", \"attributes\": { \"fanSpeed\": { \"writable\": [ \"low\", \"middle\", \"high\" ], \"supported\": [ \"low\", \"middle\", \"high\" ], \"mutability\": \"rw\" } } }, { \"name\": \"Temperature\", \"attributes\": { \"currentTemperature\": { \"mutability\": \"r\", \"max\": 30, \"min\": 18 } } }, { \"name\": \"TemperatureSetpoint\", \"attributes\": { \"targetSetpoint\": { \"min\": 18, \"max\": 30, \"mutability\": \"rw\" } } } ] }");
 		
 		
 		private String thingsName;
 		private String thingsName;
 		private String capJsonStr;
 		private String capJsonStr;
@@ -108,7 +112,21 @@ public final class THINGS_INFO {
 		ELECTRIC08					("electric08",					"PowerOutlet",		"방5콘센트", 	"방5"),
 		ELECTRIC08					("electric08",					"PowerOutlet",		"방5콘센트", 	"방5"),
 		ELECTRIC09					("electric09",					"PowerOutlet",		"방6콘센트", 	"방6"),
 		ELECTRIC09					("electric09",					"PowerOutlet",		"방6콘센트", 	"방6"),
 		ELECTRIC10					("electric10",					"PowerOutlet",		"방7콘센트", 	"방7"),
 		ELECTRIC10					("electric10",					"PowerOutlet",		"방7콘센트", 	"방7"),
-		ELECTRIC11					("electric11",					"PowerOutlet",		"방8콘센트", 	"방8")
+		ELECTRIC11					("electric11",					"PowerOutlet",		"방8콘센트", 	"방8"),
+		DOORLOCK					("doorlock",					"DoorLock",			"도어락", 	"공용부"),
+		PARKING						("parking",						"CommonParking",	"주차위치", 	"공용부"),
+		AIRSENSOR					("airsensor",					"AirMonitor",		"공기질측정기",	"거실"),
+		PARCEL						("parcel",						"CommonParcel",		"택배조회", 	"공용부"),
+		AIRCON1						("aircon_1",					"AirConditioner",	"거실에어컨",	"거실"),
+		AIRCON2						("aircon_2",					"AirConditioner",	"안방에어컨",	"안방"),
+		AIRCON3						("aircon_3",					"AirConditioner",	"방1에어컨",	"방1"),
+		AIRCON4						("aircon_4",					"AirConditioner",	"방2에어컨",	"방2"),
+		AIRCON5						("aircon_5",					"AirConditioner",	"방3에어컨",	"방3"),
+		AIRCON6						("aircon_6",					"AirConditioner",	"방4에어컨",	"방4"),
+		AIRCON7						("aircon_7",					"AirConditioner",	"방5에어컨",	"방5"),
+		AIRCON8						("aircon_8",					"AirConditioner",	"방6에어컨", 	"방6"),
+		AIRCON9						("aircon_9",					"AirConditioner",	"방7에어컨", 	"방7"),
+		AIRCON10					("aircon_10",					"AirConditioner",	"방8에어컨", 	"방8")
 		;
 		;
 		
 		
 		
 		
@@ -174,15 +192,18 @@ public final class THINGS_INFO {
 		VENTIL			(7, "VENTIL", 			"AirVentilator", 		"환기시스템"),
 		VENTIL			(7, "VENTIL", 			"AirVentilator", 		"환기시스템"),
 		NOTICE			(8, "NOTICE", 			"InformationReporter", 	"공지사항"),
 		NOTICE			(8, "NOTICE", 			"InformationReporter", 	"공지사항"),
 		VOTE			(9, "VOTE", 			"vote", 				"주민투표"),
 		VOTE			(9, "VOTE", 			"vote", 				"주민투표"),
-		PARKING			(10, "PARKING", 		"parking", 				"주차위치"),
-		PARCEL			(11, "PARCEL", 			"parcel", 				"택배도착"),
+		PARKING			(10, "PARKING", 		"CommonParking", 		"주차위치"),
+		PARCEL			(11, "PARCEL", 			"CommonParcel", 		"택배"),
 		HEMS			(12, "HEMS", 			"hems", 				"원격검침"),
 		HEMS			(12, "HEMS", 			"hems", 				"원격검침"),
 		CARIN			(13, "CARIN", 			"carin", 				"주차관제(입차/출차)"),
 		CARIN			(13, "CARIN", 			"carin", 				"주차관제(입차/출차)"),
 		ELEVATOR		(14, "elevator",		"ElevatorCaller", 		"엘리베이터"),
 		ELEVATOR		(14, "elevator",		"ElevatorCaller", 		"엘리베이터"),
 		VISITOR			(15, "VISITOR", 		"visitor", 				"방문자"),
 		VISITOR			(15, "VISITOR", 		"visitor", 				"방문자"),
 		EMERGENCY		(16, "EMERGENCY", 		"emergency", 			"방범"),
 		EMERGENCY		(16, "EMERGENCY", 		"emergency", 			"방범"),
-		LIGHTDIMMER		(17, "DIMMER", 	"LightDimmer", 			"디밍제어 조명"),
-		COLORLIGHTDIMMER(18, "COLORDIMMER", "ColorLightDimmer", 	"색온도 디밍제어 조명")
+		LIGHTDIMMER		(17, "DIMMER", 			"LightDimmer", 			"디밍제어 조명"),
+		COLORLIGHTDIMMER(18, "COLORDIMMER", 	"ColorLightDimmer", 	"색온도 디밍제어 조명"),
+		AIRSENSOR		(19, "AIRSENSOR",	 	"AirMonitor", 			"공기 질 측정기"),
+		AIRCON			(20, "AIRCON",	 		"AirConditioner", 		"에어컨")
+		
 		;
 		;
 		
 		
 		private int endPoint;
 		private int endPoint;
@@ -268,7 +289,8 @@ public final class THINGS_INFO {
 	}
 	}
 	
 	
 	public static boolean isAvailableThings(String modelId) {
 	public static boolean isAvailableThings(String modelId) {
-		return Arrays.asList("livinglight", "light", "temper", "ventil", "gas", "electric").contains(modelId);
+//		return Arrays.asList("aircon", "airsensor", "electric", "gas", "light", "livinglight", "temper", "ventil").contains(modelId);
+		return Arrays.asList("electric", "gas", "light", "livinglight", "temper", "ventil").contains(modelId);
 	}
 	}
 	
 	
 }
 }

+ 57 - 4
src/main/java/com/icontrols/kakao/common/utils/CommonUtil.java

@@ -4,6 +4,7 @@ import java.io.BufferedReader;
 import java.io.File;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.IOException;
+import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Base64;
 import java.util.Base64;
 import java.util.Calendar;
 import java.util.Calendar;
@@ -81,10 +82,16 @@ public class CommonUtil {
 			map = mapper.readValue(jsonString, new TypeReference<Map<String, Object>>(){});
 			map = mapper.readValue(jsonString, new TypeReference<Map<String, Object>>(){});
 		} catch (JsonParseException e) {
 		} catch (JsonParseException e) {
 			e.printStackTrace();
 			e.printStackTrace();
+			logger.error("jsonToMap Error" + e.getMessage());
 		} catch (JsonMappingException e) {
 		} catch (JsonMappingException e) {
 			e.printStackTrace();
 			e.printStackTrace();
+			logger.error("jsonToMap Error" + e.getMessage());
 		} catch (IOException e) {
 		} catch (IOException e) {
 			e.printStackTrace();
 			e.printStackTrace();
+			logger.error("jsonToMap Error" + e.getMessage());
+		} catch (Exception e) {
+			e.printStackTrace();
+			logger.error("jsonToMap Error" + e.getMessage());
 		}
 		}
 		return map;
 		return map;
 	}
 	}
@@ -116,6 +123,17 @@ public class CommonUtil {
 		SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
 		SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
 		return sdFormat.format(cal.getTime());
 		return sdFormat.format(cal.getTime());
 	}
 	}
+	
+	public static String convertTimeToKakaoTime(String strTime) throws Exception {
+		String result = "";
+		SimpleDateFormat sdFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+		
+		Date n = sdFormat.parse(strTime);
+		SimpleDateFormat sdFormat2 = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
+		String tmpTime = sdFormat2.format(n);
+		result = tmpTime.split("_")[0] + "T" + tmpTime.split("_")[1] + "Z";
+		return result;
+	}
 	// String 타입의 두 날짜를 비교하기
 	// String 타입의 두 날짜를 비교하기
 	// -1 면 startTime 이 크다.
 	// -1 면 startTime 이 크다.
 	// 0 이면 두 시간은 같다
 	// 0 이면 두 시간은 같다
@@ -142,17 +160,21 @@ public class CommonUtil {
 	 */
 	 */
 	public static int validateHeaderAuth(String authorization) {
 	public static int validateHeaderAuth(String authorization) {
 		String splitStr[] = authorization.split(" ");
 		String splitStr[] = authorization.split(" ");
-		
+		logger.debug("[validateHeaderAuth]-[splitStr length]=" + splitStr.length);
 		// splitStr length 가 2가 아니면 유효하지 않은 authorization 값
 		// splitStr length 가 2가 아니면 유효하지 않은 authorization 값
-		if(splitStr.length != 2) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
+//		if(splitStr.length != 2) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
 		
 		
+		logger.debug("[validateHeaderAuth]-[splitStr[0]]=" + splitStr[0]);
 		// splitStr이 Basic이 아니어도 에러
 		// splitStr이 Basic이 아니어도 에러
-		if(!splitStr[0].equals("Basic")) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
+//		if(!splitStr[0].equals("Basic")) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
+		
+		String encodeStr = splitStr.length != 2?splitStr[0]:splitStr[1];
 		
 		
 		//Base64 디코드
 		//Base64 디코드
-		String decodeStr = new String(Base64.getDecoder().decode(splitStr[1]));
+		String decodeStr = new String(Base64.getDecoder().decode(encodeStr));
 		
 		
 		splitStr = decodeStr.split(":");
 		splitStr = decodeStr.split(":");
+		logger.debug("[validateHeaderAuth]-[splitStr length 2]=" + splitStr.length);
 		// splitStr length 가 2가 아니면 유효하지 않은 authorization 값
 		// splitStr length 가 2가 아니면 유효하지 않은 authorization 값
 		if(splitStr.length != 2) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
 		if(splitStr.length != 2) return KAKAO_RESULT.FAIL_INVALID_CLIENT;
 		
 		
@@ -227,4 +249,35 @@ public class CommonUtil {
 		
 		
 		return jsonStr;
 		return jsonStr;
 	}
 	}
+	
+	public static int getAirQuaility(String type, int value) {
+		switch(type) {
+			case "co2":
+				if(value < 1001)
+					return 0;
+				else if(value <= 1001 && value < 1501)
+					return 1;
+				else if(value <= 1501 && value < 3001)
+					return 2;
+				else return 3;
+			case "pm10":
+				if(value < 31)
+					return 0;
+				else if(value <= 31 && value < 81)
+					return 1;
+				else if(value <= 81 && value < 150)
+					return 2;
+				else return 3;
+			case "pm2_5":
+				if(value < 16)
+					return 0;
+				else if(value <= 16 && value < 36)
+					return 1;
+				else if(value <= 36 && value < 76)
+					return 2;
+				else return 3;
+		}
+		
+		return 0;
+	}
 }
 }

+ 15 - 13
src/main/java/com/icontrols/kakao/controller/KakaoDanziController.java

@@ -75,19 +75,21 @@ public class KakaoDanziController {
 					params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_CLIENT));
 					params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_CLIENT));
 					retMap = params;
 					retMap = params;
 				}else {
 				}else {
-					String authorization =  request.getHeader(SERVERINFO.HEADER_AUTH);
-					logger.info("[Authorization] - "+authorization);
-					int validation_auth = CommonUtil.validateHeaderAuth(authorization);
-					if(validation_auth != KAKAO_RESULT.SUCCESS) {
-						params.remove(KEY_INFO.HTTP_KEY_BODY);
-						params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_CLIENT));
-						logger.error("[Authorization ERROR - ] - ["+authorization+"] - " + KAKAO_RESULT.FAIL_INVALID_CLIENT);
-						retMap = params;
-					}else {
-						// 카카오 홈과 유저의 인증 완료시 카카오에서 전송해주는 API TYPE
-						// 건내받은 토큰 정보를 DB에 저장해야 한다.
-						retMap = kakaoService.request_grantAuthorization(params);
-					}
+//					String authorization =  request.getHeader(SERVERINFO.HEADER_AUTH);
+//					logger.info("[Authorization] - "+authorization);
+//					int validation_auth = CommonUtil.validateHeaderAuth(authorization);
+//					logger.info("[validation_auth]-" + validation_auth);
+//					if(validation_auth != KAKAO_RESULT.SUCCESS) {
+//						params.remove(KEY_INFO.HTTP_KEY_BODY);
+//						params.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_CLIENT));
+//						logger.error("[Authorization ERROR - ] - ["+authorization+"] - " + KAKAO_RESULT.FAIL_INVALID_CLIENT);
+//						retMap = params;
+//					}else {
+//						// 카카오 홈과 유저의 인증 완료시 카카오에서 전송해주는 API TYPE
+//						// 건내받은 토큰 정보를 DB에 저장해야 한다.
+//						retMap = kakaoService.request_grantAuthorization(params);
+//					}
+					retMap = kakaoService.request_grantAuthorization(params);
 				}
 				}
 			} else if(reqType.equals(TYPE.REQ_TYPE.DISCOVER.getTypeName())) {
 			} else if(reqType.equals(TYPE.REQ_TYPE.DISCOVER.getTypeName())) {
 				// 월패드와 연결된 기기목록들을 요청하는 API TYPE
 				// 월패드와 연결된 기기목록들을 요청하는 API TYPE

+ 130 - 0
src/main/java/com/icontrols/kakao/controller/TestController.java

@@ -0,0 +1,130 @@
+package com.icontrols.kakao.controller;
+
+import java.util.Map;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.icontrols.kakao.common.KAKAO_RESULT;
+import com.icontrols.kakao.common.KEY_INFO;
+import com.icontrols.kakao.common.SERVERINFO;
+import com.icontrols.kakao.common.TYPE;
+import com.icontrols.kakao.common.utils.CommonUtil;
+import com.icontrols.kakao.common.utils.JWTUtils2;
+import com.icontrols.kakao.service.AccessTokenService;
+import com.icontrols.kakao.service.KakaoExtensionService;
+import com.icontrols.kakao.socket.wallpad.KakaoMsgHandler;
+
+@RestController
+public class TestController {
+	
+	private static final Logger logger = LoggerFactory.getLogger(TestController.class);
+	
+	@Autowired
+	KakaoExtensionService kakaoService;
+	
+	@Autowired
+	AccessTokenService accessTokenService;
+	
+	@Resource(name="kakaoMsgHandler")
+	KakaoMsgHandler kMsgHandler;
+	
+	/**
+	 * 중계 서버에서 오는 요청을         분기 처리를 진행한다.
+	 * @param header
+	 * @return
+	 */
+	@RequestMapping(value = "/test/Discover", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> getRequest(
+														@RequestHeader HttpHeaders header,
+														@RequestBody Map<String, Object> params,
+														HttpServletResponse response, 
+														HttpServletRequest request
+														) throws Exception{
+		logger.debug("[REQUEST] - "+ params.toString());
+		// 받은 요청에 대해 반환 해주는 결과 JSON 객체
+		Map<String, Object> retMap = null;
+		try {
+			// 월패드와 연결된 기기목록들을 요청하는 API TYPE
+			retMap = kakaoService.request_discover_test(params);
+		}catch(Exception e) {
+			logger.error("[Unknown ERROR]" + e.getMessage());
+			logger.error(e.getStackTrace().toString());
+			e.printStackTrace();
+			retMap = KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL);
+		}finally {
+			retMap.remove(KEY_INFO.HTTP_KEY_AUTHORIZATION);
+		}
+		
+		logger.info("[RESULT] - " + CommonUtil.mapToString(retMap));
+		return retMap;
+	}
+	
+	@RequestMapping(value = "/test/airconAttr", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> getRequestAirConAttr(
+														@RequestHeader HttpHeaders header,
+														@RequestBody Map<String, Object> params,
+														HttpServletResponse response, 
+														HttpServletRequest request
+														) throws Exception{
+		logger.debug("[REQUEST] - "+ params.toString());
+		// 받은 요청에 대해 반환 해주는 결과 JSON 객체
+		Map<String, Object> retMap = null;
+		try {
+			// 월패드와 연결된 기기목록들을 요청하는 API TYPE
+			int dong = Integer.parseInt((String)params.get("dong"));
+			int ho = Integer.parseInt((String)params.get("ho"));
+			retMap = kMsgHandler.getAirconCapability(dong, ho);
+		}catch(Exception e) {
+			logger.error("[Unknown ERROR]" + e.getMessage());
+			logger.error(e.getStackTrace().toString());
+			e.printStackTrace();
+			retMap = KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL);
+		}finally {
+			retMap.remove(KEY_INFO.HTTP_KEY_AUTHORIZATION);
+		}
+		
+		logger.info("[RESULT] - " + CommonUtil.mapToString(retMap));
+		return retMap;
+	}
+	
+	@RequestMapping(value = "/test/airconTemp", method = RequestMethod.POST, consumes = "application/json")
+	public @ResponseBody Map<String, Object> getRequestAirConTemp(
+														@RequestHeader HttpHeaders header,
+														@RequestBody Map<String, Object> params,
+														HttpServletResponse response, 
+														HttpServletRequest request
+														) throws Exception{
+		logger.debug("[REQUEST] - "+ params.toString());
+		// 받은 요청에 대해 반환 해주는 결과 JSON 객체
+		Map<String, Object> retMap = null;
+		try {
+			// 월패드와 연결된 기기목록들을 요청하는 API TYPE
+			int dong = Integer.parseInt((String)params.get("dong"));
+			int ho = Integer.parseInt((String)params.get("ho"));
+			String endpointId = (String)params.get("endpointId");
+			retMap = kMsgHandler.getAirconEnableTemperature(dong, ho, endpointId);
+		}catch(Exception e) {
+			logger.error("[Unknown ERROR]" + e.getMessage());
+			logger.error(e.getStackTrace().toString());
+			e.printStackTrace();
+			retMap = KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL);
+		}finally {
+			retMap.remove(KEY_INFO.HTTP_KEY_AUTHORIZATION);
+		}
+		logger.info("[RESULT] - " + CommonUtil.mapToString(retMap));
+		return retMap;
+	}
+}

+ 80 - 0
src/main/java/com/icontrols/kakao/entity/ParcelInfo.java

@@ -0,0 +1,80 @@
+package com.icontrols.kakao.entity;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "tbl_parcel_info")
+public class ParcelInfo implements Serializable {
+	private static final long serialVersionUID = 3239676041983738509L;
+	
+	@Id
+	@Column(name = "dongho_id")
+	private String donghoId;
+	
+	@Column(name = "quantity")
+	private int quantity;
+	
+	@Column(name = "lst_upt_evt")
+	private String lstUptEvt;
+	
+	@Column(name = "lst_upt_time")
+	private String lstUptTime;
+
+	
+	public ParcelInfo() {
+		super();
+	}
+
+	public ParcelInfo(String donghoId, int quantity, String lstUptEvt, String lstUptTime) {
+		super();
+		this.donghoId = donghoId;
+		this.quantity = quantity;
+		this.lstUptEvt = lstUptEvt;
+		this.lstUptTime = lstUptTime;
+	}
+
+	public String getDonghoId() {
+		return donghoId;
+	}
+
+	public void setDonghoId(String donghoId) {
+		this.donghoId = donghoId;
+	}
+
+	public int getQuantity() {
+		return quantity;
+	}
+
+	public void setQuantity(int quantity) {
+		this.quantity = quantity;
+	}
+
+	public String getLstUptEvt() {
+		return lstUptEvt;
+	}
+
+	public void setLstUptEvt(String lstUptEvt) {
+		this.lstUptEvt = lstUptEvt;
+	}
+
+	public String getLstUptTime() {
+		return lstUptTime;
+	}
+
+	public void setLstUptTime(String lstUptTime) {
+		this.lstUptTime = lstUptTime;
+	}
+
+	@Override
+	public String toString() {
+		return "ParcelInfo [donghoId=" + donghoId + ", quantity=" + quantity + ", lstUptEvt=" + lstUptEvt
+				+ ", lstUptTime=" + lstUptTime + "]";
+	}
+	
+	
+}

+ 13 - 0
src/main/java/com/icontrols/kakao/entity/UserInfo.java

@@ -11,6 +11,8 @@ import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
 import javax.persistence.ManyToOne;
 import javax.persistence.Table;
 import javax.persistence.Table;
 
 
+import org.hibernate.annotations.ColumnDefault;
+
 @Entity
 @Entity
 @Table(name = "tbl_user_info")
 @Table(name = "tbl_user_info")
 public class UserInfo implements Serializable{
 public class UserInfo implements Serializable{
@@ -48,6 +50,10 @@ public class UserInfo implements Serializable{
 	@Column(name = "delete_type")
 	@Column(name = "delete_type")
 	private String deleteType;
 	private String deleteType;
 	
 	
+	@Column(name = "prev_user_id")
+	@ColumnDefault("0")
+	private long prevUserId;
+	
 	@ManyToOne
 	@ManyToOne
 	@JoinColumn(name = "dongho_id")
 	@JoinColumn(name = "dongho_id")
 	private DonghoInfo donghoInfo;
 	private DonghoInfo donghoInfo;
@@ -123,6 +129,13 @@ public class UserInfo implements Serializable{
 	public void setDeleteType(String deleteType) {
 	public void setDeleteType(String deleteType) {
 		this.deleteType = deleteType;
 		this.deleteType = deleteType;
 	}
 	}
+	
+	public long getPrevUserId() {
+		return prevUserId;
+	}
+	public void setPrevUserId(long prevUserId) {
+		this.prevUserId = prevUserId;
+	}
 	public DonghoInfo getDonghoInfo() {
 	public DonghoInfo getDonghoInfo() {
 		return donghoInfo;
 		return donghoInfo;
 	}
 	}

+ 11 - 0
src/main/java/com/icontrols/kakao/repo/ParcelInfoRepository.java

@@ -0,0 +1,11 @@
+package com.icontrols.kakao.repo;
+
+import java.util.List;
+
+import org.springframework.data.repository.CrudRepository;
+
+import com.icontrols.kakao.entity.ParcelInfo;
+
+public interface ParcelInfoRepository extends CrudRepository<ParcelInfo, Long> {
+	List<ParcelInfo> findByDonghoId(String donghoId);
+}

+ 1 - 0
src/main/java/com/icontrols/kakao/service/AccessTokenService.java

@@ -3,6 +3,7 @@ package com.icontrols.kakao.service;
 import java.util.Map;
 import java.util.Map;
 
 
 public interface AccessTokenService {
 public interface AccessTokenService {
+	public boolean isTokenExpired(String accessToken) throws Exception;
 	public int validate(String accessToken) throws Exception;
 	public int validate(String accessToken) throws Exception;
 	public Map<String, Object> create(Map<String, Object> param) throws Exception;
 	public Map<String, Object> create(Map<String, Object> param) throws Exception;
 	public Map<String, Object> refresh(Map<String, Object> param) throws Exception;
 	public Map<String, Object> refresh(Map<String, Object> param) throws Exception;

+ 2 - 0
src/main/java/com/icontrols/kakao/service/KakaoExtensionService.java

@@ -7,6 +7,8 @@ public interface KakaoExtensionService {
 	
 	
 	public Map<String, Object> request_discover(Map<String, Object> param) throws Exception;
 	public Map<String, Object> request_discover(Map<String, Object> param) throws Exception;
 	
 	
+	public Map<String, Object> request_discover_test(Map<String, Object> param) throws Exception;
+	
 	public Map<String, Object> request_queryState(Map<String, Object> param) throws Exception;
 	public Map<String, Object> request_queryState(Map<String, Object> param) throws Exception;
 	
 	
 	public Map<String, Object> request_updateState(Map<String, Object> param) throws Exception;
 	public Map<String, Object> request_updateState(Map<String, Object> param) throws Exception;

+ 42 - 19
src/main/java/com/icontrols/kakao/service/impl/AccessTokenServiceImpl.java

@@ -34,6 +34,11 @@ public class AccessTokenServiceImpl implements AccessTokenService {
 	@Autowired
 	@Autowired
 	UserInfoRepository userInfoRepo;
 	UserInfoRepository userInfoRepo;
 	
 	
+	@Override
+	public boolean isTokenExpired(String accessToken) throws Exception {
+		
+		return true;
+	}
 	@Override
 	@Override
 	public int validate(String accessToken) throws Exception {
 	public int validate(String accessToken) throws Exception {
 		Map<String, Object> retMap = new HashMap<String, Object>();
 		Map<String, Object> retMap = new HashMap<String, Object>();
@@ -53,8 +58,10 @@ public class AccessTokenServiceImpl implements AccessTokenService {
 		UserInfo userInfo = uList.get(0);
 		UserInfo userInfo = uList.get(0);
 		logger.info("[validate] - " + userInfo.toString());
 		logger.info("[validate] - " + userInfo.toString());
 		if(userInfo.getDeleteFlag().equals("Y")) {
 		if(userInfo.getDeleteFlag().equals("Y")) {
-			logger.error("[ERROR] - 연동 해지 토큰 - " + accessToken);
-			return KAKAO_RESULT.FAIL_UNLINKEXTENSION_SERVICE;
+			if(!userInfo.getDeleteType().equalsIgnoreCase("expiredToken")) {
+				logger.error("[ERROR] - 연동 해지 토큰 - " + accessToken);
+				return KAKAO_RESULT.FAIL_UNLINKEXTENSION_SERVICE;
+			}
 		}
 		}
 		
 		
 		// 2. JWT 검증
 		// 2. JWT 검증
@@ -159,21 +166,34 @@ public class AccessTokenServiceImpl implements AccessTokenService {
 			}
 			}
 			
 			
 			if(userInfo.getDeleteFlag().equals("Y")) {
 			if(userInfo.getDeleteFlag().equals("Y")) {
-				Map<String, Object> errMap = new HashMap<String, Object>();
-				errMap.put("code", KAKAO_RESULT.CODE.FAIL_UNLINKEXTENSION_SERVICE.getResultCode());
-				errMap.put("message", KAKAO_RESULT.CODE.FAIL_UNLINKEXTENSION_SERVICE.getMessage());
+				//토큰이 삭제된 이유가 토큰 만료면 에러가 아니다.
+				if(!userInfo.getDeleteType().equalsIgnoreCase("expiredToken")) {
+					Map<String, Object> errMap = new HashMap<String, Object>();
+					errMap.put("code", KAKAO_RESULT.CODE.FAIL_UNLINKEXTENSION_SERVICE.getResultCode());
+					errMap.put("message", KAKAO_RESULT.CODE.FAIL_UNLINKEXTENSION_SERVICE.getMessage());
 //				errMap.put("message", ERROR_UNLINK_SERVICE);
 //				errMap.put("message", ERROR_UNLINK_SERVICE);
-				
-				retMap.put("error", errMap);
-				logger.error("[AccessTokenService]-[REFRESH] - [ERROR] - UNLINK TOKEN - " + userInfo.getDeleteType());
-				return retMap;
+					
+					retMap.put("error", errMap);
+					logger.error("[AccessTokenService]-[REFRESH] - [ERROR] - UNLINK TOKEN - " + userInfo.getDeleteType());
+					return retMap;
+				}
 			}
 			}
+			// 기존 토큰은 삭제하고 새로운 토큰을 생성한다.
+			userInfo.setDeleteFlag("Y");
+			userInfo.setDeleteType("expiredToken");
+			
 			
 			
-			userInfo.setAccessToken(newAccessToken);
-			userInfo.setRefreshToken(newRefreshToken);
+			UserInfo newUserInfo = new UserInfo(newAccessToken, newRefreshToken, userInfo.getDonghoInfo());
+			newUserInfo.setKakaoAccessToken(userInfo.getKakaoAccessToken());
+			newUserInfo.setKakaoExpiresTime(userInfo.getKakaoExpiresTime());
+			newUserInfo.setKakaoRefreshToken(userInfo.getKakaoRefreshToken());
+			newUserInfo.setKakaoTokenCreateTime(userInfo.getKakaoTokenCreateTime());
+			newUserInfo.setKakaoTokenExpireTime(userInfo.getKakaoTokenExpireTime());
+			newUserInfo.setPrevUserId(userInfo.getId());
 			
 			
-			// 3. 저장	
+			// 3. 저장
 			userInfoRepo.save(userInfo);
 			userInfoRepo.save(userInfo);
+			userInfoRepo.save(newUserInfo);
 		}catch(Exception e) {
 		}catch(Exception e) {
 			Map<String, Object> errMap = new HashMap<String, Object>();
 			Map<String, Object> errMap = new HashMap<String, Object>();
 			errMap.put("code", KAKAO_RESULT.CODE.FAIL_INTERNAL.getResultCode());
 			errMap.put("code", KAKAO_RESULT.CODE.FAIL_INTERNAL.getResultCode());
@@ -229,15 +249,18 @@ public class AccessTokenServiceImpl implements AccessTokenService {
 				return retMap;
 				return retMap;
 			}
 			}
 			
 			
+			// 삭제된 사용자인지 확인 (기타 다른 이유로 삭제 된건지 확인)
 			if(userInfo.getDeleteFlag().equals("Y")) {
 			if(userInfo.getDeleteFlag().equals("Y")) {
-				Map<String, Object> errMap = new HashMap<String, Object>();
-				errMap.put("code", KAKAO_RESULT.CODE.FAIL_UNLINKEXTENSION_SERVICE.getResultCode());
-				errMap.put("message", KAKAO_RESULT.CODE.FAIL_UNLINKEXTENSION_SERVICE.getMessage());
+				if(!userInfo.getDeleteType().equalsIgnoreCase("expiredToken")) {
+					Map<String, Object> errMap = new HashMap<String, Object>();
+					errMap.put("code", KAKAO_RESULT.CODE.FAIL_UNLINKEXTENSION_SERVICE.getResultCode());
+					errMap.put("message", KAKAO_RESULT.CODE.FAIL_UNLINKEXTENSION_SERVICE.getMessage());
 //				errMap.put("message", ERROR_UNLINK_SERVICE);
 //				errMap.put("message", ERROR_UNLINK_SERVICE);
-				
-				retMap.put("error", errMap);
-				logger.error("[AccessTokenService]-[REFRESH] - [ERROR] - UNLINK TOKEN - " + userInfo.getDeleteType());
-				return retMap;
+					
+					retMap.put("error", errMap);
+					logger.error("[AccessTokenService]-[REFRESH] - [ERROR] - UNLINK TOKEN - " + userInfo.getDeleteType());
+					return retMap;
+				}
 			}else {
 			}else {
 				Map<String, Object> body = new HashMap<String, Object>();
 				Map<String, Object> body = new HashMap<String, Object>();
 				
 				

+ 16 - 4
src/main/java/com/icontrols/kakao/service/impl/KakaoExtensionServiceImpl.java

@@ -18,7 +18,6 @@ import com.icontrols.kakao.common.KEY_INFO;
 import com.icontrols.kakao.common.utils.CommonUtil;
 import com.icontrols.kakao.common.utils.CommonUtil;
 import com.icontrols.kakao.common.utils.JWTUtils2;
 import com.icontrols.kakao.common.utils.JWTUtils2;
 import com.icontrols.kakao.config.AsyncConfig;
 import com.icontrols.kakao.config.AsyncConfig;
-import com.icontrols.kakao.entity.DonghoInfo;
 import com.icontrols.kakao.entity.ThingsInfo;
 import com.icontrols.kakao.entity.ThingsInfo;
 import com.icontrols.kakao.entity.UserInfo;
 import com.icontrols.kakao.entity.UserInfo;
 import com.icontrols.kakao.repo.DonghoInfoRepository;
 import com.icontrols.kakao.repo.DonghoInfoRepository;
@@ -156,6 +155,19 @@ public class KakaoExtensionServiceImpl implements KakaoExtensionService {
 		return param;
 		return param;
 	}
 	}
 	
 	
+	@Override
+	public Map<String, Object> request_discover_test(Map<String, Object> param) throws Exception{
+		int dong = Integer.parseInt((String)param.get("dong"));
+		int ho = Integer.parseInt((String)param.get("ho"));
+		logger.debug("[dong]-"+dong + "[ho]-" + ho);
+		
+		Map<String, Object> listMap = kMsgHandler.getDevicesStatusListTemp(dong, ho);
+		param.putAll(listMap);
+		
+		logger.info("[request_discover] - result = \r\n" + param);
+		return param;
+	}
+	
 	@Override
 	@Override
 	public Map<String, Object> request_queryState(Map<String, Object> param) throws Exception {
 	public Map<String, Object> request_queryState(Map<String, Object> param) throws Exception {
 		String dong_ho = JWTUtils2.getHomeIdFromToken((String)param.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
 		String dong_ho = JWTUtils2.getHomeIdFromToken((String)param.get(KEY_INFO.HTTP_KEY_AUTHORIZATION));
@@ -252,8 +264,8 @@ public class KakaoExtensionServiceImpl implements KakaoExtensionService {
 		String endpointId = (String)body.get(KEY_INFO.BODY_KEY_ENDPOINT_ID);
 		String endpointId = (String)body.get(KEY_INFO.BODY_KEY_ENDPOINT_ID);
 		String capabilityName = (String)body.get(KEY_INFO.BODY_KEY_CAPABILITY_NAME);
 		String capabilityName = (String)body.get(KEY_INFO.BODY_KEY_CAPABILITY_NAME);
 		
 		
-		logger.debug("[UPDATE] - " + body.toString());
-		
+		logger.info("[request_updateState]-[동:" + dong + "호:" + ho + "]-[" + endpointId + "]-[desired] - " + desired.toString());
+
 		List<ThingsInfo> thingsInfoList = thingsInfoRepository.findByDongAndHoAndEndpointId(Integer.toString(dong), Integer.toString(ho), endpointId);
 		List<ThingsInfo> thingsInfoList = thingsInfoRepository.findByDongAndHoAndEndpointId(Integer.toString(dong), Integer.toString(ho), endpointId);
 		
 		
 		if(thingsInfoList == null || thingsInfoList.size() == 0) {
 		if(thingsInfoList == null || thingsInfoList.size() == 0) {
@@ -264,7 +276,7 @@ public class KakaoExtensionServiceImpl implements KakaoExtensionService {
 			retMap = kMsgHandler.sendDeviceAction(dong, ho, endpointId, capabilityName, desired);
 			retMap = kMsgHandler.sendDeviceAction(dong, ho, endpointId, capabilityName, desired);
 		}
 		}
 		
 		
-		logger.debug("UPDATE-->>retMap : " + retMap.toString());
+		logger.info("[RESPONSE]-[request_updateState]-[동:" + dong + "호:" + ho + "]-" + retMap.toString());
 		
 		
 		if(retMap.containsKey(KEY_INFO.HTTP_KEY_ERROR)) {
 		if(retMap.containsKey(KEY_INFO.HTTP_KEY_ERROR)) {
 			param.remove(KEY_INFO.HTTP_KEY_BODY);
 			param.remove(KEY_INFO.HTTP_KEY_BODY);

+ 3 - 2
src/main/java/com/icontrols/kakao/socket/utils/XMLConstants.java

@@ -20,8 +20,9 @@ public class XMLConstants {
 		msg_home_device_get_status(18, "msg_home_device_get_status"), msg_visitor_list(19, "msg_visitor_list"),
 		msg_home_device_get_status(18, "msg_home_device_get_status"), msg_visitor_list(19, "msg_visitor_list"),
 		msg_car_parking_list(20, "msg_car_parking_list"),
 		msg_car_parking_list(20, "msg_car_parking_list"),
 		remote_register_new_device_auth_number(21, "remote_register_new_device_auth_number"),
 		remote_register_new_device_auth_number(21, "remote_register_new_device_auth_number"),
-		msg_total_devices_status(22, "msg_total_devices_status");
-
+		msg_total_devices_status(22, "msg_total_devices_status"),
+		msg_aircon_attributes(23, "msg_aircon_attributes");
+		
 		private int mIdx;
 		private int mIdx;
 		private String mTextXmlData;
 		private String mTextXmlData;
 
 

+ 724 - 78
src/main/java/com/icontrols/kakao/socket/wallpad/KakaoMsgHandler.java

@@ -14,6 +14,7 @@ import javax.xml.xpath.XPathFactory;
 
 
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 import org.w3c.dom.Document;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.Node;
@@ -30,8 +31,10 @@ import com.icontrols.kakao.common.utils.CommonUtil;
 import com.icontrols.kakao.common.utils.NodeUtil;
 import com.icontrols.kakao.common.utils.NodeUtil;
 import com.icontrols.kakao.common.utils.XmlUtil;
 import com.icontrols.kakao.common.utils.XmlUtil;
 import com.icontrols.kakao.config.AsyncConfig;
 import com.icontrols.kakao.config.AsyncConfig;
+import com.icontrols.kakao.entity.ParcelInfo;
 import com.icontrols.kakao.entity.ThingsInfo;
 import com.icontrols.kakao.entity.ThingsInfo;
 import com.icontrols.kakao.entity.UserInfo;
 import com.icontrols.kakao.entity.UserInfo;
+import com.icontrols.kakao.repo.ParcelInfoRepository;
 import com.icontrols.kakao.repo.ThingsInfoRepository;
 import com.icontrols.kakao.repo.ThingsInfoRepository;
 import com.icontrols.kakao.socket.utils.IPGenerator;
 import com.icontrols.kakao.socket.utils.IPGenerator;
 import com.icontrols.kakao.socket.utils.SSLClient;
 import com.icontrols.kakao.socket.utils.SSLClient;
@@ -49,6 +52,9 @@ public class KakaoMsgHandler {
 	@Resource(name="wallpadMsgSender")
 	@Resource(name="wallpadMsgSender")
 	WallpadMsgSender wallpadMsgSender;
 	WallpadMsgSender wallpadMsgSender;
 	
 	
+	@Autowired
+	ParcelInfoRepository parcelRepo;
+	
 	/**
 	/**
 	 * 카카오 전용 인증 프로세스
 	 * 카카오 전용 인증 프로세스
 	 * 월패드에 인증번호를 노출시키는 메시지를 전송한다.
 	 * 월패드에 인증번호를 노출시키는 메시지를 전송한다.
@@ -204,6 +210,8 @@ public class KakaoMsgHandler {
 						}
 						}
 					});
 					});
 			thingList.add(new ThingsInfo("elevator","ElevatorCaller", Integer.toString(dong), Integer.toString(ho)));
 			thingList.add(new ThingsInfo("elevator","ElevatorCaller", Integer.toString(dong), Integer.toString(ho)));
+			thingList.add(new ThingsInfo("parcel","CommonParcel", Integer.toString(dong), Integer.toString(ho)));
+			thingList.add(new ThingsInfo("parking","CommonParking", Integer.toString(dong), Integer.toString(ho)));
 		}catch(Exception e) {
 		}catch(Exception e) {
 			logger.error("[ERROR] - " + e.getMessage());
 			logger.error("[ERROR] - " + e.getMessage());
 			return null;
 			return null;
@@ -252,6 +260,9 @@ public class KakaoMsgHandler {
 			Node dCount =  null;
 			Node dCount =  null;
 			NodeList devList = (NodeList)xpath.evaluate("//imap/service/devinfo", doc, XPathConstants.NODESET);
 			NodeList devList = (NodeList)xpath.evaluate("//imap/service/devinfo", doc, XPathConstants.NODESET);
 			
 			
+			logger.debug("****device list***");
+			logger.debug(devList.toString());
+			
 			NodeUtil.asList(devList)
 			NodeUtil.asList(devList)
 					.stream()
 					.stream()
 					.filter(pNode -> THINGS_INFO.isAvailableThings(XmlUtil.getAttributeWithItem(pNode, "name")))
 					.filter(pNode -> THINGS_INFO.isAvailableThings(XmlUtil.getAttributeWithItem(pNode, "name")))
@@ -260,7 +271,7 @@ public class KakaoMsgHandler {
 						int childCount = Integer.parseInt(XmlUtil.getAttributeWithItem(pNode, "value"));
 						int childCount = Integer.parseInt(XmlUtil.getAttributeWithItem(pNode, "value"));
 						
 						
 						List<Map<String, Object>> childNodeInfoList = null;
 						List<Map<String, Object>> childNodeInfoList = null;
-						logger.debug("tmpModelID --> " + tmpModelId + "childCount = " + childCount);
+						logger.debug("[tmpModelID] --> " + tmpModelId + " [childCount] = " + childCount);
 						try {
 						try {
 							// subdevinfo 를 가지고 있는 THINGS일 경우는 subdevinfo 들을 다시 구해야 한다.
 							// subdevinfo 를 가지고 있는 THINGS일 경우는 subdevinfo 들을 다시 구해야 한다.
 							// 예를들어 light는 light01 ~ 05 등의 subdevinfo를 가진다.
 							// 예를들어 light는 light01 ~ 05 등의 subdevinfo를 가진다.
@@ -312,6 +323,17 @@ public class KakaoMsgHandler {
 							e.printStackTrace();
 							e.printStackTrace();
 						}
 						}
 					});
 					});
+			//aircon의 capa를 다시 조회한다.
+			// aircon은 세대별로 지원하는 모드가 다를 수 있어서 월패드의 해당 세대의 에어컨 속성을 다시 조회해서 전달해줘야 한다.
+//			Map<String, Object> airconCapaMap = getAirconCapability(dong, ho);
+//			for(int i=1; i <=10; i++) {
+//				String _tmpEndpoint = "aircon_" + i;
+//				if(body.containsKey(_tmpEndpoint)) {
+//					Map<String, Object> _tmpDeviceMap = (HashMap)body.get(_tmpEndpoint);
+//					_tmpDeviceMap.put("capabilities", airconCapaMap.get("capabilities"));
+//				}
+//			}
+			
 			// elevator는 하드코딩.
 			// elevator는 하드코딩.
 			// elevator가 존재해야 카카오홈 앱에서 엘리베이터를 제어할 수 있다.
 			// elevator가 존재해야 카카오홈 앱에서 엘리베이터를 제어할 수 있다.
 			Map<String, Object> eleMap = new HashMap<String, Object>();
 			Map<String, Object> eleMap = new HashMap<String, Object>();
@@ -324,19 +346,199 @@ public class KakaoMsgHandler {
 			eleMap.put(KEY_INFO.BODY_KEY_ADDITIONALPROP, getAdditionalProp(KEY_INFO.BODY_KEY_ROOMPRESETNAME, THINGS_INFO.DEVICE_DETAIL.ELEVATOR.getRoomName()));
 			eleMap.put(KEY_INFO.BODY_KEY_ADDITIONALPROP, getAdditionalProp(KEY_INFO.BODY_KEY_ROOMPRESETNAME, THINGS_INFO.DEVICE_DETAIL.ELEVATOR.getRoomName()));
 			body.put("elevator", eleMap);
 			body.put("elevator", eleMap);
 			
 			
-			if(SERVERINFO.KAKAO_I_WALLPAD_APPLY) {
-				Map<String, Object> wpMap = new HashMap<String, Object>();
-				String wpEndpointId = "WALLPAD_"+SERVERINFO.danzi_code+"_"+dong+"_"+ho;
-				wpMap.put("endpointId", wpEndpointId);
-				wpMap.put("name", "월패드");
-				wpMap.put("displayType", "DummyWallPad");
-				wpMap.put("capabilities", new ArrayList<>());
-				wpMap.put("state", "{}");
-				wpMap.put("additionalProperties", null);
-				
-				body.put(wpEndpointId, wpMap);
+			// 주차위치도 직접 입력해줘야 함. 
+//			Map<String, Object> parkingMap = new HashMap<String, Object>();
+//			parkingMap.put(KEY_INFO.BODY_KEY_DISPLAY_TYPE, THINGS_INFO.THINGS.PARKING.getKakaoDisplayName());
+//			parkingMap.putAll(getDeviceCapabilities(THINGS_INFO.THINGS.PARKING.getThingsName(), null));
+//			parkingMap.put(KEY_INFO.BODY_KEY_ENDPOINT_ID, THINGS_INFO.DEVICE_DETAIL.PARKING.getModelId());
+//			parkingMap.put(KEY_INFO.BODY_KEY_NAME, THINGS_INFO.THINGS.PARKING.getDesc());
+//
+//			// 주차위치를 월패드에 물어봐서 입력해주어야함.
+//			parkingMap.put(KEY_INFO.BODY_KEY_STATE, getParkingList(dong, ho));
+//			parkingMap.put(KEY_INFO.BODY_KEY_ADDITIONALPROP, getAdditionalProp(KEY_INFO.BODY_KEY_ROOMPRESETNAME, THINGS_INFO.DEVICE_DETAIL.PARKING.getRoomName()));
+//			body.put("parking", parkingMap);
+			
+			// 택배 정보를 조회해서 알려주어야 함.
+//			Map<String, Object> parcelMap = getParcelList(dong, ho);
+//			
+//			if(parcelMap != null)
+//				body.put("parcel", parcelMap);
+			
+//			if(SERVERINFO.KAKAO_I_WALLPAD_APPLY) {
+//				Map<String, Object> wpMap = new HashMap<String, Object>();
+//				String wpEndpointId = "WALLPAD_"+SERVERINFO.danzi_code+"_"+dong+"_"+ho;
+//				wpMap.put("endpointId", wpEndpointId);
+//				wpMap.put("name", "월패드");
+//				wpMap.put("displayType", "DummyWallPad");
+//				wpMap.put("capabilities", new ArrayList<>());
+//				wpMap.put("state", "{}");
+//				wpMap.put("additionalProperties", null);
+//				
+//				body.put(wpEndpointId, wpMap);
+//			}
+			
+		}catch(Exception e) {
+			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
+			logger.error("[ERROR] - " + e.getMessage());
+		}
+		
+		metadata.put("aptCode", SERVERINFO.danzi_code);
+		retMap.put(KEY_INFO.HTTP_KEY_METADATA, metadata); 
+		retMap.put(KEY_INFO.HTTP_KEY_UNREACHABLEIDS, unreachableIds);
+		retMap.put(KEY_INFO.HTTP_KEY_BODY, body);
+		
+		return retMap;
+	}
+	
+	public Map<String, Object> getDevicesStatusListTemp(int dong, int ho) {
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		Map<String, String> metadata = new HashMap<String, String>();
+		Map<String, Object> body = new HashMap<String, Object>();
+		List<String> unreachableIds = new ArrayList<String>();
+		
+		try {
+			Document doc = new WallpadMsgSender().remoteDeviceStatusList(dong, ho);
+			
+			if(doc == null) {
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+				logger.error("[ERROR]-[Document is Null] - [" + dong + "-" + ho + "]");
+				return retMap;
+			}
+			
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			Node iMap = (Node)xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+			String xmlResult = XmlUtil.getAttributeWithItem(iMap, "result");
+			
+			logger.debug("[KakaoMsgHandler - getDeviceList] - xmlResult -> " + xmlResult);
+			
+			if(!xmlResult.equalsIgnoreCase("ok")) {
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+				logger.error("[KakaoMsgHandler - getDeviceList] - [" + dong + "]-[" + ho + "] xmlResult not ok \r\n" + xmlResult);
+				
+				return retMap;
 			}
 			}
 			
 			
+			// 월패드와 연결된 상위 THINGS 갯수 확인
+			Node dCount =  null;
+			NodeList devList = (NodeList)xpath.evaluate("//imap/service/devinfo", doc, XPathConstants.NODESET);
+			
+			logger.debug("****device list***");
+			logger.debug(devList.toString());
+			
+			NodeUtil.asList(devList)
+					.stream()
+					.filter(pNode -> THINGS_INFO.isAvailableThings(XmlUtil.getAttributeWithItem(pNode, "name")))
+					.forEach(pNode -> {
+						String tmpModelId = XmlUtil.getAttributeWithItem(pNode, "name");
+						int childCount = Integer.parseInt(XmlUtil.getAttributeWithItem(pNode, "value"));
+						
+						List<Map<String, Object>> childNodeInfoList = null;
+						logger.debug("[tmpModelID] --> " + tmpModelId + " [childCount] = " + childCount);
+						try {
+							// subdevinfo 를 가지고 있는 THINGS일 경우는 subdevinfo 들을 다시 구해야 한다.
+							// 예를들어 light는 light01 ~ 05 등의 subdevinfo를 가진다.
+
+							// livinglight와 light는 -> dimmer와 colorTemp를 조절이 가능한 조명이 포함될 수 있다.
+							// 전등의 경우 월패드에서는 크게 livinglight 또는 light 로 나뉘고 속성은 같은반변 
+							// 카카오에서는 light, LightDimmer, ColorLightDimmer 세가지로 나뉜다.
+							// 월패드의 status는 (전원)/(디밍값)/(색상온도값) 으로 구성되며 
+							// 전원을 제외하고 각 지원하지 않는 기능은 null로 전달된다. 
+							// LightDimmer는 월패드에서 status가 on/40/null 로  status가 구성되고
+							// ColorLightDimmer는 월패드에서 status가 on/40/10 의 형태 또는 status가 on/null/10 의형태로 구성된다. 
+							// status의 구성요소에 맞게 capability가 구성되어야 한다. 
+							
+							
+							if(tmpModelId.equalsIgnoreCase("light") || tmpModelId.equalsIgnoreCase("electric")) {
+								for(int j=1; j <= childCount; j++) {
+									String childName = j < 10 ? tmpModelId+"0"+j : tmpModelId+j;
+									Node childNode = (Node)xpath.evaluate("//imap/service/devinfo/*[@name='"+ childName +"']", doc, XPathConstants.NODE);
+									logger.debug("childName = " + childName + " // j = " + j);
+									
+									if(childNode == null) continue;
+									
+									String tmpValue = XmlUtil.getAttributeWithItem(childNode, "value");
+									
+									if(tmpValue == null) continue;
+									
+									int tmpchildCount = Integer.parseInt(tmpValue);
+									
+									NodeList childList = (NodeList)xpath.evaluate("//imap/service/devinfo/subdevinfo/*[@name='"+ childName +"']", doc, XPathConstants.NODESET);
+									
+									childNodeInfoList = getChildDeviceInfo(tmpchildCount, childList);
+									logger.debug("child-->>" + childNodeInfoList.toString());
+									if(childNodeInfoList != null)
+										childNodeInfoList.forEach(tMap -> { body.putAll(tMap); });
+								}
+							}else {
+								NodeList childList = (NodeList)xpath.evaluate("//imap/service/devinfo/*[@name='"+ tmpModelId +"']", doc, XPathConstants.NODESET);
+								
+								childNodeInfoList = getChildDeviceInfo(childCount, childList);
+								
+								logger.debug("child-->>" + childNodeInfoList.toString());
+								
+								if(childNodeInfoList != null)
+									childNodeInfoList.forEach(tMap -> { body.putAll(tMap); });
+							}
+						}catch(Exception e) {
+							retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
+							logger.error("[ERROR] at stream - " + e.getMessage());
+							e.printStackTrace();
+						}
+					});
+			
+			Map<String, Object> airconCapaMap = getAirconCapability(dong, ho);
+			for(int i=1; i <=10; i++) {
+				String _tmpEndpoint = "aircon_" + i;
+				if(body.containsKey(_tmpEndpoint)) {
+					Map<String, Object> _tmpDeviceMap = (HashMap)body.get(_tmpEndpoint);
+					_tmpDeviceMap.put("capabilities", airconCapaMap.get("capabilities"));
+				}
+			}
+			
+			
+			// elevator는 하드코딩.
+			// elevator가 존재해야 카카오홈 앱에서 엘리베이터를 제어할 수 있다.
+			Map<String, Object> eleMap = new HashMap<String, Object>();
+			
+			eleMap.put(KEY_INFO.BODY_KEY_DISPLAY_TYPE, THINGS_INFO.THINGS.ELEVATOR.getKakaoDisplayName());
+			eleMap.putAll(getDeviceCapabilities(THINGS_INFO.THINGS.ELEVATOR.getThingsName(), null));
+			eleMap.put(KEY_INFO.BODY_KEY_ENDPOINT_ID, THINGS_INFO.DEVICE_DETAIL.ELEVATOR.getModelId());
+			eleMap.put(KEY_INFO.BODY_KEY_NAME, THINGS_INFO.THINGS.ELEVATOR.getDesc());
+			eleMap.put(KEY_INFO.BODY_KEY_STATE, new HashMap<String, Object>());
+			eleMap.put(KEY_INFO.BODY_KEY_ADDITIONALPROP, getAdditionalProp(KEY_INFO.BODY_KEY_ROOMPRESETNAME, THINGS_INFO.DEVICE_DETAIL.ELEVATOR.getRoomName()));
+			body.put("elevator", eleMap);
+			
+			// 주차위치도 직접 입력해줘야 함. 
+			Map<String, Object> parkingMap = new HashMap<String, Object>();
+			parkingMap.put(KEY_INFO.BODY_KEY_DISPLAY_TYPE, THINGS_INFO.THINGS.PARKING.getKakaoDisplayName());
+			parkingMap.putAll(getDeviceCapabilities(THINGS_INFO.THINGS.PARKING.getThingsName(), null));
+			parkingMap.put(KEY_INFO.BODY_KEY_ENDPOINT_ID, THINGS_INFO.DEVICE_DETAIL.PARKING.getModelId());
+			parkingMap.put(KEY_INFO.BODY_KEY_NAME, THINGS_INFO.THINGS.PARKING.getDesc());
+
+			// 주차위치를 월패드에 물어봐서 입력해주어야함.
+			parkingMap.put(KEY_INFO.BODY_KEY_STATE, getParkingList(dong, ho));
+			parkingMap.put(KEY_INFO.BODY_KEY_ADDITIONALPROP, getAdditionalProp(KEY_INFO.BODY_KEY_ROOMPRESETNAME, THINGS_INFO.DEVICE_DETAIL.PARKING.getRoomName()));
+			body.put("parking", parkingMap);
+			
+			// 택배 정보를 조회해서 알려주어야 함.
+			Map<String, Object> parcelMap = getParcelList(dong, ho);
+			
+			if(parcelMap != null)
+				body.put("parcel", parcelMap);
+			
+//			if(SERVERINFO.KAKAO_I_WALLPAD_APPLY) {
+//				Map<String, Object> wpMap = new HashMap<String, Object>();
+//				String wpEndpointId = "WALLPAD_"+SERVERINFO.danzi_code+"_"+dong+"_"+ho;
+//				wpMap.put("endpointId", wpEndpointId);
+//				wpMap.put("name", "월패드");
+//				wpMap.put("displayType", "DummyWallPad");
+//				wpMap.put("capabilities", new ArrayList<>());
+//				wpMap.put("state", "{}");
+//				wpMap.put("additionalProperties", null);
+//				
+//				body.put(wpEndpointId, wpMap);
+//			}
+			
 		}catch(Exception e) {
 		}catch(Exception e) {
 			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
 			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INTERNAL));
 			logger.error("[ERROR] - " + e.getMessage());
 			logger.error("[ERROR] - " + e.getMessage());
@@ -350,6 +552,288 @@ public class KakaoMsgHandler {
 		return retMap;
 		return retMap;
 	}
 	}
 	
 	
+	
+	private Map<String, Object>getParkingList(int dong, int ho){
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		try {
+			Document doc = new WallpadMsgSender().remoteGetParkingList(dong, ho);
+			if(doc == null) {
+				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+				logger.error("[ERROR]-[getParkingList]-[Document is Null] - [" + dong + "-" + ho + "]");
+				return retMap;
+			}
+			
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			Node iMap = (Node)xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+			String xmlResult = XmlUtil.getAttributeWithItem(iMap, "result");
+			
+			logger.debug("[KakaoMsgHandler - getParkingList] - xmlResult -> " + xmlResult);
+			
+			if(!xmlResult.equalsIgnoreCase("ok")) {
+//				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+				logger.error("[KakaoMsgHandler - getParkingList] - [" + dong + "]-[" + ho + "] xmlResult not ok \r\n" + xmlResult);
+				retMap.put("parkingLocations", new ArrayList<>());
+				return retMap;
+			}
+			Node parkingList = (Node)xpath.evaluate("//imap/service/parking_list",doc, XPathConstants.NODE);
+			int parkingListCnt = Integer.parseInt(XmlUtil.getAttributeWithItem(parkingList, "total_count"));
+			
+			logger.debug("[KakaoMsgHandler - getParkingList] - 1 parkingListCnt = " + parkingListCnt);
+			
+			if(parkingListCnt == 0) return retMap;
+			
+			List<Map<String,Object>> parkingLocations = new ArrayList<>();
+			NodeUtil.asList((NodeList)xpath.evaluate("//imap/service/parking_list/parking_info", doc, XPathConstants.NODESET))
+					.stream()
+					.forEach(pNode ->{
+						String carNum = XmlUtil.getAttributeWithItem(pNode, "car_num");
+						String location = XmlUtil.getAttributeWithItem(pNode, "location");
+						Map<String, Object> _parkingInfo = new HashMap<String, Object>();
+						
+						_parkingInfo.put("carno", carNum);
+						_parkingInfo.put("name", location);
+						_parkingInfo.put("parked", true);
+						
+						parkingLocations.add(_parkingInfo);
+					});
+			retMap.put("parkingLocations", parkingLocations);
+		}catch(Exception e) {
+			logger.error("[ERROR]-[getParkingList]-" + e.getMessage());
+		}
+		
+		logger.info("[parkingList]-[ret]-" + retMap.toString());
+		
+		return retMap;
+	}
+	
+	private Map<String, Object>getParcelList(int dong, int ho){
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		try {
+			List<ParcelInfo> list = parcelRepo.findByDonghoId(dong+"/"+ho);
+			// 해당 세대의 조회된 택배DB가 없을 경우 DB를 생성한다.
+			ParcelInfo parcelObj = null;
+			if(list.size() == 0) {
+				parcelObj = new ParcelInfo(dong+"/"+ho, 0, null, null);
+				parcelRepo.save(parcelObj);
+			}else {
+				parcelObj = list.get(0);
+			}
+			Map<String, Object> stateMap = new HashMap<>();
+			List<Object> parcelList = new ArrayList<>();
+			logger.info("[" + dong + "/" + ho +"]-[parcel]-" + parcelObj.toString());
+			
+			for(int i=0; i < parcelObj.getQuantity(); i++) {
+				Map<String, Object> _obj = new HashMap<>();
+				_obj.put("name", "택배 보관함");
+				_obj.put("lastDeliveredTime", parcelObj.getLstUptTime());
+				parcelList.add(_obj);
+			}
+			
+			stateMap.put("parcelLocations", parcelList);
+			
+			retMap.put(KEY_INFO.BODY_KEY_DISPLAY_TYPE, THINGS_INFO.THINGS.PARCEL.getKakaoDisplayName());
+			retMap.putAll(getDeviceCapabilities(THINGS_INFO.THINGS.PARCEL.getThingsName(), null));
+			retMap.put(KEY_INFO.BODY_KEY_ENDPOINT_ID, THINGS_INFO.DEVICE_DETAIL.PARCEL.getModelId());
+			retMap.put(KEY_INFO.BODY_KEY_NAME, THINGS_INFO.THINGS.PARCEL.getDesc());
+			retMap.put(KEY_INFO.BODY_KEY_STATE, stateMap);
+			retMap.put(KEY_INFO.BODY_KEY_ADDITIONALPROP, getAdditionalProp(KEY_INFO.BODY_KEY_ROOMPRESETNAME, THINGS_INFO.DEVICE_DETAIL.PARKING.getRoomName()));
+		}catch(Exception e) {
+			logger.error("[ERROR]-[getParcelList]-" + e.getMessage());
+			e.printStackTrace();
+			return null;
+		}
+		return retMap;
+	}
+	
+	public Map<String, Object>getAirconCapability(int dong, int ho){
+		String jsonString = THINGS_INFO.THINGS_CAPABILITY.AIRCON.getCapJsonStr();
+		
+		Map<String, Object> capabilities = CommonUtil.jsonToMap(jsonString);
+		try {
+			Document doc = new WallpadMsgSender().remoteGetAirconAttributes(dong, ho);
+			if(doc == null) {
+				logger.error("[ERROR]-[getAirconCapability]-[Document is Null] - [" + dong + "-" + ho + "]");
+				return capabilities;
+			}
+			
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			Node iMap = (Node)xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+			String xmlResult = XmlUtil.getAttributeWithItem(iMap, "result");
+			
+			logger.debug("[KakaoMsgHandler - 	private] - xmlResult -> " + xmlResult);
+			
+			if(!xmlResult.equalsIgnoreCase("ok")) {
+//				retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+				logger.error("[KakaoMsgHandler - getAirconCapability] - [" + dong + "]-[" + ho + "] xmlResult not ok \r\n" + xmlResult);
+				return capabilities;
+			}
+			
+			Node attributes = (Node)xpath.evaluate("//imap/service/attributes",doc, XPathConstants.NODE);
+			int totalAirconCount = Integer.parseInt(XmlUtil.getAttributeWithItem(attributes, "total_count"));
+			
+			logger.debug("[KakaoMsgHandler - getAirconCapability] - totalAirconCount = " + totalAirconCount);
+			
+			Node coolTempAttributes = (Node)xpath.evaluate("//imap/service/attributes/cool_temperature",doc, XPathConstants.NODE);
+			int minCoolTemperature = Integer.parseInt(XmlUtil.getAttributeWithItem(coolTempAttributes, "min"));
+			int maxCoolTemperature = Integer.parseInt(XmlUtil.getAttributeWithItem(coolTempAttributes, "max"));
+			
+			Node heatTempAttributes = (Node)xpath.evaluate("//imap/service/attributes/heat_temperature",doc, XPathConstants.NODE);
+			int minHeatTemperature = Integer.parseInt(XmlUtil.getAttributeWithItem(heatTempAttributes, "min"));
+			int maxHeatTemperature = Integer.parseInt(XmlUtil.getAttributeWithItem(heatTempAttributes, "max"));
+			
+			int minTemperature = minCoolTemperature<=minHeatTemperature?minCoolTemperature:minHeatTemperature;
+			int maxTemperature = maxCoolTemperature>=maxHeatTemperature?maxCoolTemperature:maxHeatTemperature;
+			
+			logger.debug("[KakaoMsgHandler - getAirconCapability] - minTemperature = " + minTemperature + " - maxTemperature =" + maxTemperature);
+			
+			Node modeAttributes = (Node)xpath.evaluate("//imap/service/attributes/mode",doc, XPathConstants.NODE);
+			String strModeOption = XmlUtil.getElemetTextValue(modeAttributes);
+			String[] arrModeOption = strModeOption.split("/");
+			logger.debug("[KakaoMsgHandler - getAirconCapability] - arrModeOption = " + arrModeOption);
+			
+			Node airFlowAttributes = (Node)xpath.evaluate("//imap/service/attributes/air_flow",doc, XPathConstants.NODE);
+			String strAirFlowOption = XmlUtil.getElemetTextValue(airFlowAttributes);
+			String[] arrAirFlowOption = strAirFlowOption.split("/");
+			logger.debug("[KakaoMsgHandler - getAirconCapability] - arrAirFlowOption = " + arrAirFlowOption);
+			
+			List<HashMap<String, Object>> capabilityList = (ArrayList)capabilities.get("capabilities");
+			logger.debug("[KakaoMsgHandler - getAirconCapability] - capabilityList = " + capabilityList.toString());
+			
+			for(HashMap<String, Object> capa:capabilityList) {
+				String capaName = (String) capa.get("name");
+				logger.debug("[KakaoMsgHandler - getAirconCapability] - capaName = " + capaName);
+				Map<String, Object> attr = (HashMap)capa.get("attributes");
+				logger.debug("[KakaoMsgHandler - getAirconCapability] - attr = " + attr.toString());
+				if(capaName.equalsIgnoreCase("AirConditioner")) {
+					Map<String, Object> modeCapa = (HashMap)attr.get("mode");
+					logger.debug("[KakaoMsgHandler - getAirconCapability] - modeCapa = " + modeCapa.toString());
+					
+					List<String> modeAttrList = new ArrayList();
+					for(String mode:arrModeOption) {
+						logger.debug("[KakaoMsgHandler - getAirconCapability] - modeCapa-detail = " + mode);
+						if(mode.equalsIgnoreCase("auto"))
+							modeAttrList.add("auto");
+						else if(mode.equalsIgnoreCase("cool"))
+							modeAttrList.add("cool");
+						else if(mode.equalsIgnoreCase("heat"))
+							modeAttrList.add("heat");
+						else if(mode.equalsIgnoreCase("fan"))
+							modeAttrList.add("fan_only");
+						else if(mode.equalsIgnoreCase("dehum"))
+							modeAttrList.add("dehumidify");
+					}
+					modeCapa.clear();
+					modeCapa.put("writable", modeAttrList);
+					modeCapa.put("supported", modeAttrList);
+					modeCapa.put("mutability", "rw");
+				}else if(capaName.equalsIgnoreCase("AirFlow")) {
+					Map<String, Object> airFlowCapa = (HashMap)attr.get("fanSpeed");
+					List<String> airFlowAttrList = new ArrayList();
+					for(String mode:arrAirFlowOption) {
+						if(mode.equalsIgnoreCase("auto"))
+							airFlowAttrList.add("auto");
+						else if(mode.equalsIgnoreCase("low"))
+							airFlowAttrList.add("low");
+						else if(mode.equalsIgnoreCase("mid"))
+							airFlowAttrList.add("middle");
+						else if(mode.equalsIgnoreCase("high"))
+							airFlowAttrList.add("high");
+					}
+					airFlowCapa.clear();
+					airFlowCapa.put("writable", airFlowAttrList);
+					airFlowCapa.put("supported", airFlowAttrList);
+					airFlowCapa.put("mutability", "rw");
+				}else if(capaName.equalsIgnoreCase("Temperature")) {
+					Map<String, Object> tempAttr = new HashMap<>();
+					tempAttr.put("mutability", "r");
+					tempAttr.put("max", maxTemperature);
+					tempAttr.put("min", minTemperature);
+					tempAttr.put("mutability", "r");
+					attr.clear();
+					attr.put("currentTemperature", tempAttr);
+					
+				}else if(capaName.equalsIgnoreCase("TemperatureSetpoint")) {
+					Map<String, Object> tempAttr = new HashMap<>();
+					tempAttr.put("mutability", "r");
+					tempAttr.put("max", maxTemperature);
+					tempAttr.put("min", minTemperature);
+					tempAttr.put("mutability", "r");
+					attr.clear();
+					attr.put("targetSetpoint", tempAttr);
+				}
+			}
+		}catch(Exception e) {
+			logger.error("[ERROR]-[getAirconCapability]-[Exception]-" + e.getMessage());
+		}
+		
+		logger.info("[getAirconCapability]-" + capabilities.toString());
+		return capabilities;
+	}
+	
+	public static Map<String, Object>getAirconEnableTemperature(int dong, int ho, String endpointId){
+		// 디바이스의 현재 상태를 먼저 조회한다. 
+		// 디바이스의 모드를 먼저 확인한다.
+		// 디바이스의 설정 온도 가능 범위를 확인한다. 
+		// 디바이스가 냉방이면 냉방의 가능범위를 응답한다.
+		// 디바이스가 난방이면 난방의 가능범위를 응답한다.
+		// 디바이스가 그외 모드이면 냉방과 난방의 교집합으로 응답한다.
+		Map<String, Object> retMap = new HashMap<String, Object>();
+		try {
+			Map<String, Object> deviceStateMap= getDeviceStatus(dong, ho, endpointId);
+			Map<String, Object> statusMap = (HashMap)deviceStateMap.get(endpointId);
+			if(statusMap.containsKey("mode")) {
+				String mode = (String)statusMap.get("mode");
+				
+				Document doc = new WallpadMsgSender().remoteGetAirconAttributes(dong, ho);
+				if(doc == null) {
+					logger.error("[ERROR]-[getAirconEnableTemperature]-[Document is Null] - [" + dong + "-" + ho + "]");
+					return null;
+				}
+				
+				XPath xpath = XPathFactory.newInstance().newXPath();
+				Node iMap = (Node)xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+				String xmlResult = XmlUtil.getAttributeWithItem(iMap, "result");
+				
+				logger.debug("[KakaoMsgHandler - 	private] - xmlResult -> " + xmlResult);
+				
+				if(!xmlResult.equalsIgnoreCase("ok")) {
+//					retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
+					logger.error("[KakaoMsgHandler - getAirconEnableTemperature] - [" + dong + "]-[" + ho + "] xmlResult not ok \r\n" + xmlResult);
+					return null;
+				}
+				
+				Node coolTempAttributes = (Node)xpath.evaluate("//imap/service/attributes/cool_temperature",doc, XPathConstants.NODE);
+				int minCoolTemperature = Integer.parseInt(XmlUtil.getAttributeWithItem(coolTempAttributes, "min"));
+				int maxCoolTemperature = Integer.parseInt(XmlUtil.getAttributeWithItem(coolTempAttributes, "max"));
+				
+				Node heatTempAttributes = (Node)xpath.evaluate("//imap/service/attributes/heat_temperature",doc, XPathConstants.NODE);
+				int minHeatTemperature = Integer.parseInt(XmlUtil.getAttributeWithItem(heatTempAttributes, "min"));
+				int maxHeatTemperature = Integer.parseInt(XmlUtil.getAttributeWithItem(heatTempAttributes, "max"));
+				
+				
+				if(mode.equalsIgnoreCase("cool")) {
+					retMap.put("min", minCoolTemperature);
+					retMap.put("max", maxCoolTemperature);
+				}else if(mode.equalsIgnoreCase("heat")) {
+					retMap.put("min", minHeatTemperature);
+					retMap.put("max", maxHeatTemperature);
+				}else {
+					retMap.put("min", minCoolTemperature >= minHeatTemperature?minCoolTemperature:minHeatTemperature);
+					retMap.put("max", maxCoolTemperature <= maxHeatTemperature?maxCoolTemperature:maxHeatTemperature);
+				}
+			}else {
+				/// 에러처리..
+				logger.error("[ERROR]-[getAirconEnableTemperature]-["+dong+"]-["+ho+"]-["+endpointId+"]-" + deviceStateMap.toString());
+				return null;
+			}
+		}catch(Exception e) {
+			logger.error("[ERROR]-[getAirconEnableTemperature]-[Exception]-" + e.getMessage());
+			return null;
+		}
+		return retMap;
+	}
+	
+	
 	/**
 	/**
 	 * 동 호 정보 EndpointID 로 월패드에 상태 조회 메세지를 전송하고 결과를 반환 받는다. 
 	 * 동 호 정보 EndpointID 로 월패드에 상태 조회 메세지를 전송하고 결과를 반환 받는다. 
 	 * @param dong
 	 * @param dong
@@ -357,7 +841,7 @@ public class KakaoMsgHandler {
 	 * @param endpointId
 	 * @param endpointId
 	 * @return
 	 * @return
 	 */
 	 */
-	public Map<String, Object> getDeviceStatus(int dong, int ho, String endpointId){
+	public static Map<String, Object> getDeviceStatus(int dong, int ho, String endpointId){
 		Map<String, Object> retMap = new HashMap<String, Object>();
 		Map<String, Object> retMap = new HashMap<String, Object>();
 		String modelId = "";
 		String modelId = "";
 		int devNum = 1;
 		int devNum = 1;
@@ -438,7 +922,7 @@ public class KakaoMsgHandler {
 			return retMap;
 			return retMap;
 		}
 		}
 		
 		
-		String action = KakaoMsgHandler.setDeviceActionStr(endpointId, modelId, actionName, desired);
+		String action = KakaoMsgHandler.setDeviceActionStr(dong, ho, endpointId, modelId, actionName, desired);
 		
 		
 		if(action.length() == 0) {
 		if(action.length() == 0) {
 			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_REQ));
 			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_INVALID_REQ));
@@ -446,13 +930,23 @@ public class KakaoMsgHandler {
 			return retMap;
 			return retMap;
 		}
 		}
 		
 		
-		Document doc = new WallpadMsgSender().remoteDeviceControl(
+		Document doc = null;
+		for(int i=0; i < 3; i++) {
+			doc = new WallpadMsgSender().remoteDeviceControl(
 																dong, 
 																dong, 
 																ho, 
 																ho, 
 																modelId, 
 																modelId, 
 																devNum, 
 																devNum, 
 																action
 																action
 															);
 															);
+			if(doc != null) break;
+			try {
+				Thread.sleep(500);
+			} catch (InterruptedException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+		}
 		
 		
 		if(doc == null) {
 		if(doc == null) {
 			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
 			retMap.put(KEY_INFO.HTTP_KEY_ERROR, KAKAO_RESULT.getErrorInfo(KAKAO_RESULT.FAIL_UNREACHABLE));
@@ -607,9 +1101,25 @@ public class KakaoMsgHandler {
 		case THINGS_INFO.THINGS_ELECPLUG:
 		case THINGS_INFO.THINGS_ELECPLUG:
 			jsonString = THINGS_INFO.THINGS_CAPABILITY.ELECTRIC.getCapJsonStr();
 			jsonString = THINGS_INFO.THINGS_CAPABILITY.ELECTRIC.getCapJsonStr();
 			break;
 			break;
+		case THINGS_INFO.THINGS_PARKING:
+			jsonString = THINGS_INFO.THINGS_CAPABILITY.PARKING.getCapJsonStr();
+			break;
+		case THINGS_INFO.THINGS_PARCEL:
+			jsonString = THINGS_INFO.THINGS_CAPABILITY.PARCEL.getCapJsonStr();
+			break;
+		case THINGS_INFO.THINGS_AIR_MONITOR:
+			jsonString = THINGS_INFO.THINGS_CAPABILITY.AIRSENSOR.getCapJsonStr();
+			break;
+		case THINGS_INFO.THINGS_AIRCON:
+			jsonString = THINGS_INFO.THINGS_CAPABILITY.AIRCON.getCapJsonStr();
+			
+			break;
+		default:
+			logger.error("Can not found device capability - " + modelId + " - " + things.getEndPoint());
+			break;
 		}
 		}
 		Map<String, Object> capabilities = CommonUtil.jsonToMap(jsonString);
 		Map<String, Object> capabilities = CommonUtil.jsonToMap(jsonString);
-		
+		logger.info("[" + modelId + "]-endpoint - " + things.getEndPoint() + "-capability="+ capabilities.toString());
 		return capabilities;
 		return capabilities;
 	}
 	}
 	
 	
@@ -618,6 +1128,8 @@ public class KakaoMsgHandler {
 		additionalPropMap.put(key, value);
 		additionalPropMap.put(key, value);
 		return additionalPropMap;
 		return additionalPropMap;
 	}
 	}
+	
+	
 	public List<ThingsInfo> getChildThingsInfoList(int dong, int ho, int childCount, NodeList childList) {
 	public List<ThingsInfo> getChildThingsInfoList(int dong, int ho, int childCount, NodeList childList) {
 		List<ThingsInfo> thingsList = new ArrayList<>();
 		List<ThingsInfo> thingsList = new ArrayList<>();
 		
 		
@@ -685,10 +1197,12 @@ public class KakaoMsgHandler {
 						String cNodeName = XmlUtil.getAttributeWithItem(cNode, "name");
 						String cNodeName = XmlUtil.getAttributeWithItem(cNode, "name");
 						String cNodeDevNum = XmlUtil.getAttributeWithItem(cNode, "dev_num");
 						String cNodeDevNum = XmlUtil.getAttributeWithItem(cNode, "dev_num");
 						String status = XmlUtil.getElemetTextValue(cNode);
 						String status = XmlUtil.getElemetTextValue(cNode);
+						String[] strSplit = status.split("/");
+						
 						String endpointId = cNodeName+"_"+cNodeDevNum;
 						String endpointId = cNodeName+"_"+cNodeDevNum;
 						
 						
 						if(cNodeName.contains("light")) {
 						if(cNodeName.contains("light")) {
-							String[] strSplit = status.split("/");
+							
 							// split 이 안됐다면 on off만 되는 예전 방식으로 전등 상태를 받았다는 뜻.
 							// split 이 안됐다면 on off만 되는 예전 방식으로 전등 상태를 받았다는 뜻.
 							if(strSplit.length > 1) {
 							if(strSplit.length > 1) {
 								if(!strSplit[1].equals("null") && strSplit[2].equals("null")) {
 								if(!strSplit[1].equals("null") && strSplit[2].equals("null")) {
@@ -702,8 +1216,8 @@ public class KakaoMsgHandler {
 							}
 							}
 						}
 						}
 						
 						
-			 			String tmpModelId = cNodeName.equals("temper")?endpointId:cNodeName;
-						logger.debug("tmpModelId = " + tmpModelId + " || endpointID = " + endpointId);
+			 			String tmpModelId = cNodeName.equals("temper") || cNodeName.equals("aircon") ?endpointId:cNodeName;
+						logger.debug("tmpModelId = " + tmpModelId + " || endpointID = " + endpointId + " || status = " + status);
 			 			
 			 			
 						
 						
 						THINGS_INFO.DEVICE_DETAIL detailDev = THINGS_INFO.getDeviceInfo(tmpModelId, status);
 						THINGS_INFO.DEVICE_DETAIL detailDev = THINGS_INFO.getDeviceInfo(tmpModelId, status);
@@ -714,15 +1228,16 @@ public class KakaoMsgHandler {
 			    		deviceInfo.put(KEY_INFO.BODY_KEY_NAME, detailDev.getDisplayName()+cNodeDevNum);
 			    		deviceInfo.put(KEY_INFO.BODY_KEY_NAME, detailDev.getDisplayName()+cNodeDevNum);
 			    		deviceInfo.put(KEY_INFO.BODY_KEY_DISPLAY_TYPE, detailDev.getDisplayType());
 			    		deviceInfo.put(KEY_INFO.BODY_KEY_DISPLAY_TYPE, detailDev.getDisplayType());
 			    		
 			    		
-			    		
 			    		deviceInfo.putAll(getDeviceCapabilities(endpointId, status));
 			    		deviceInfo.putAll(getDeviceCapabilities(endpointId, status));
 			    		deviceInfo.put(KEY_INFO.BODY_KEY_STATE, setDeviceState(endpointId, status));
 			    		deviceInfo.put(KEY_INFO.BODY_KEY_STATE, setDeviceState(endpointId, status));
 			    		deviceInfo.put(KEY_INFO.BODY_KEY_ADDITIONALPROP, getAdditionalProp(KEY_INFO.BODY_KEY_ROOMPRESETNAME,  detailDev.getRoomName()));
 			    		deviceInfo.put(KEY_INFO.BODY_KEY_ADDITIONALPROP, getAdditionalProp(KEY_INFO.BODY_KEY_ROOMPRESETNAME,  detailDev.getRoomName()));
 			    		
 			    		
 			    		deviceMap.put(endpointId, deviceInfo);
 			    		deviceMap.put(endpointId, deviceInfo);
-			    		deviceList.add(deviceMap);
 			    		
 			    		
+			    		if(!strSplit[0].equals("null"))
+			    			deviceList.add(deviceMap);
 					}catch(Exception e) {
 					}catch(Exception e) {
+						logger.error("[getChildDeviceInfo]-" + e.getMessage());
 						e.printStackTrace();
 						e.printStackTrace();
 					}
 					}
 					
 					
@@ -787,7 +1302,7 @@ public class KakaoMsgHandler {
 	 * @return
 	 * @return
 	 */
 	 */
 	
 	
-	public static String setDeviceActionStr(String endPointId, String modelId, String capability, Map<String, Object> desired) {
+	public static String setDeviceActionStr(int dong, int ho, String endPointId, String modelId, String capability, Map<String, Object> desired) {
 		StringBuffer sb = new StringBuffer();
 		StringBuffer sb = new StringBuffer();
 		
 		
 		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(endPointId);
 		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(endPointId);
@@ -809,43 +1324,46 @@ public class KakaoMsgHandler {
 				
 				
 				break;
 				break;
 			case THINGS_INFO.THINGS_LIGHT_DIMMER:
 			case THINGS_INFO.THINGS_LIGHT_DIMMER:
+				String [] arr_dimm_light_act = {"on", "null", "null"};
 				
 				
-				if(capability.equals("Power") && desired.containsKey("power")) {
-					String tmpState = (String)desired.get("power");
-					if(tmpState.equals("on") || tmpState.equals("off")) {
-						sb.append(tmpState);
-						sb.append("/null/null");
-					}
-				}
+				if(desired.containsKey("power"))
+					arr_dimm_light_act[0] = (String)desired.get("power");
+					
 				
 				
 				if(desired.containsKey("brightness")) {
 				if(desired.containsKey("brightness")) {
-					brightness = ((int)desired.get("brightness"))/10 == 0 ? 1: ((int)desired.get("brightness"))/10;
-					sb.append("on").append("/").append(brightness).append("/null");
+//					brightness = ((int)desired.get("brightness"))/10 == 0 ? 1: ((int)desired.get("brightness"))/10;
+					brightness = (int)desired.get("brightness");
+					if(brightness < 1)
+						arr_dimm_light_act[1] = Integer.toString(1);
+					else if(brightness > 10)
+						arr_dimm_light_act[1] = Integer.toString(10);
+					else
+						arr_dimm_light_act[1] = Integer.toString(brightness);
 				}
 				}
 				
 				
+				sb.append(arr_dimm_light_act[0]).append("/")
+				  .append(arr_dimm_light_act[1]).append("/")
+				  .append(arr_dimm_light_act[2]);
 				break;
 				break;
 				
 				
 			case THINGS_INFO.THINGS_COLOR_LIGHT_DIMMER:
 			case THINGS_INFO.THINGS_COLOR_LIGHT_DIMMER:
+				String [] arr_color_dimm_light_act = {"on", "null", "null"};
 				
 				
-				if(capability.equals("Power") && desired.containsKey("power")) {
-					String tmpState = (String)desired.get("power");
-					if(tmpState.equals("on") || tmpState.equals("off")) {
-						sb.append(tmpState);
-					}
-				}
-				
+				if(desired.containsKey("power"))
+					arr_color_dimm_light_act[0] = (String)desired.get("power");
+					
 				
 				
 				if(desired.containsKey("brightness")) {
 				if(desired.containsKey("brightness")) {
-					brightness = ((int)desired.get("brightness"))/10 == 0 ? 1: ((int)desired.get("brightness"))/10;
-					if(desired.containsKey("power"))
-						sb.append("/").append(brightness);
+//					brightness = ((int)desired.get("brightness"))/10 == 0 ? 1: ((int)desired.get("brightness"))/10;
+					brightness = (int)desired.get("brightness");
+					if(brightness < 1)
+						arr_color_dimm_light_act[1] = Integer.toString(1);
+					else if(brightness > 10)
+						arr_color_dimm_light_act[1] = Integer.toString(10);
 					else
 					else
-						sb.append("on/").append(brightness);
-				}else {
-					sb.append("/null");
+						arr_color_dimm_light_act[1] = Integer.toString(brightness);
 				}
 				}
 				
 				
-				
 				if(desired.containsKey("colorTemperature")) {
 				if(desired.containsKey("colorTemperature")) {
 //					colorTemper = ((int)desired.get("colorTemperature"))/1000 == 0 ? 1:((int)desired.get("colorTemperature"))/1000;
 //					colorTemper = ((int)desired.get("colorTemperature"))/1000 == 0 ? 1:((int)desired.get("colorTemperature"))/1000;
 					int tmpColorTemp = (int)desired.get("colorTemperature");
 					int tmpColorTemp = (int)desired.get("colorTemperature");
@@ -854,22 +1372,12 @@ public class KakaoMsgHandler {
 					else if(tmpColorTemp > 5700) tmpColorTemp = 5700;
 					else if(tmpColorTemp > 5700) tmpColorTemp = 5700;
 					
 					
 					colorTemper = (tmpColorTemp)/300 - 9;
 					colorTemper = (tmpColorTemp)/300 - 9;
-					if(desired.containsKey("power"))
-						sb.append("/").append(colorTemper);
-					else {
-						if(desired.containsKey("brightness")) {
-							sb.append("/").append(colorTemper);	
-						}else {
-							sb = new StringBuffer();
-							sb.append("on/null/").append(colorTemper);
-						}
-						
-					}
-				}else {
-					sb.append("/null");
+					arr_color_dimm_light_act[2] = Integer.toString(colorTemper);
 				}
 				}
 				
 				
-				
+				sb.append(arr_color_dimm_light_act[0]).append("/")
+				  .append(arr_color_dimm_light_act[1]).append("/")
+				  .append(arr_color_dimm_light_act[2]);
 				break;
 				break;
 				
 				
 			case THINGS_INFO.THINGS_TEMPER:
 			case THINGS_INFO.THINGS_TEMPER:
@@ -895,23 +1403,20 @@ public class KakaoMsgHandler {
 				break;
 				break;
 				
 				
 			case THINGS_INFO.THINGS_VENTIL:
 			case THINGS_INFO.THINGS_VENTIL:
-				if(capability.equals("Power") && desired.containsKey("power")) {
-					String tmpState = (String)desired.get("power");
-					if(tmpState.equals("on") || tmpState.equals("off")) sb.append(tmpState);
-					
-					if(tmpState.equals("on") && desired.containsKey("fanSpeed")) {
-						String tmpFanSpeed = (String)desired.get("fanSpeed");
-						if(tmpFanSpeed.equals("low") || tmpFanSpeed.equals("middle") || tmpFanSpeed.equals("high"))
-							sb.append(desired.get("fanSpeed"));
-					}
+				String []arr_ventil_act = {"on", "null"};
+				if(desired.containsKey("power")) {
+					arr_ventil_act[0] = (String) desired.get("power");
 				}
 				}
-				
-				if(capability.equals("AirFlow") && desired.containsKey("fanSpeed")) {
-					String tmpFanSpeed = (String)desired.get("fanSpeed");
-					
-					if(tmpFanSpeed.equals("low") || tmpFanSpeed.equals("middle") || tmpFanSpeed.equals("high"))
-						sb.append("on/").append(desired.get("fanSpeed"));
+				if(desired.containsKey("fanSpeed")) {
+					if("low".equals((String) desired.get("fanSpeed")))
+						arr_ventil_act[1] = "low";
+					else if("middle".equals((String) desired.get("fanSpeed")))
+						arr_ventil_act[1] = "mid";
+					else if("high".equals((String) desired.get("fanSpeed")))
+						arr_ventil_act[1] = "high";
 				}
 				}
+				sb.append(arr_ventil_act[0]).append("/")
+				  .append(arr_ventil_act[1]);
 				break;
 				break;
 				
 				
 			case THINGS_INFO.THINGS_ELECPLUG: // 지원하지 않음
 			case THINGS_INFO.THINGS_ELECPLUG: // 지원하지 않음
@@ -928,15 +1433,103 @@ public class KakaoMsgHandler {
 					if("down".equalsIgnoreCase((String)desired.get("callDirection"))) {
 					if("down".equalsIgnoreCase((String)desired.get("callDirection"))) {
 						sb.append(desired.get("callDirection"));
 						sb.append(desired.get("callDirection"));
 					}
 					}
+					
+					logger.info("[Elevator Call]-" + sb.toString());
+				}
+				break;
+			case THINGS_INFO.THINGS_AIRCON:
+				//                         파워     설정온도    모드    풍속      풍향
+				String[] arr_aircon_act = {"on", "null", "null", "null", "null"};
+				
+				if(desired.containsKey("power")) arr_aircon_act[0] = (String) desired.get("power");
+				
+				if(desired.containsKey("targetSetpoint")) {
+					Map<String, Object> tempMap = getAirconEnableTemperature(dong, ho, endPointId);
+					int minTemp = (int) tempMap.get("min");
+					int maxTemp = (int) tempMap.get("max");
+					double dobReqTemp = (double) desired.get("targetSetpoint");
+					int reqTemp = (int) dobReqTemp;
+					
+//					int reqTemp = ((Double) desired.get("targetSetpoint")).intValue();
+					if(reqTemp < minTemp) arr_aircon_act[1] = Integer.toString(minTemp);
+					else if(reqTemp > maxTemp) arr_aircon_act[1] = Integer.toString(maxTemp);
+					else arr_aircon_act[1] = Integer.toString(reqTemp);
 				}
 				}
+				
+				if(desired.containsKey("mode")) {
+					String modeVal = (String) desired.get("mode");
+					
+					if(modeVal.equals("auto"))
+						arr_aircon_act[2] = "auto";
+					else if(modeVal.equals("cool"))
+						arr_aircon_act[2] = "cool";
+					else if(modeVal.equals("heat"))
+						arr_aircon_act[2] = "heat";
+					else if(modeVal.equals("dehumidify"))
+						arr_aircon_act[2] = "dehum";
+					else if(modeVal.equals("fan_only"))
+						arr_aircon_act[2] = "fan";
+				}
+				
+				if(desired.containsKey("fanSpeed")) {
+					String modeVal = (String) desired.get("fanSpeed");
+					
+					if(modeVal.equals("auto"))
+						arr_aircon_act[3] = "auto";
+					else if(modeVal.equals("low"))
+						arr_aircon_act[3] = "low";
+					else if(modeVal.equals("middle"))
+						arr_aircon_act[3] = "mid";
+					else if(modeVal.equals("high"))
+						arr_aircon_act[3] = "high";
+				}
+				
+				sb.append(arr_aircon_act[0]).append("/");
+				sb.append(arr_aircon_act[1]).append("/");
+				sb.append(arr_aircon_act[2]).append("/");
+				sb.append(arr_aircon_act[3]).append("/null");
+				
+				logger.info("[aircon]-action str = " + sb.toString());
+				break;
 			}
 			}
 		}catch(Exception e) {
 		}catch(Exception e) {
-			
+			logger.error("[Action Str Error]-" + e.getMessage());
+			e.printStackTrace();
 		}
 		}
 		
 		
 		return sb.toString();
 		return sb.toString();
 	}
 	}
 	
 	
+	private static int getAirQuaility(String type, int value) {
+		switch(type) {
+			case "co2":
+				if(value < 1001)
+					return 0;
+				else if(value <= 1001 && value < 1501)
+					return 1;
+				else if(value <= 1501 && value < 3001)
+					return 2;
+				else return 3;
+			case "pm10":
+				if(value < 31)
+					return 0;
+				else if(value <= 31 && value < 81)
+					return 1;
+				else if(value <= 81 && value < 150)
+					return 2;
+				else return 3;
+			case "pm2_5":
+				if(value < 16)
+					return 0;
+				else if(value <= 16 && value < 36)
+					return 1;
+				else if(value <= 36 && value < 76)
+					return 2;
+				else return 3;
+		}
+		
+		return 0;
+	}
 	/**
 	/**
 	 * 상태조회 이후 월패드에서 받은 상태 값을 디바이스의 속성에 맞게 파싱하고 
 	 * 상태조회 이후 월패드에서 받은 상태 값을 디바이스의 속성에 맞게 파싱하고 
 	 * Map에 <"속성명", "상태값"> 형태로 담아준다. 
 	 * Map에 <"속성명", "상태값"> 형태로 담아준다. 
@@ -949,6 +1542,9 @@ public class KakaoMsgHandler {
 		
 		
 		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(modelId);
 		THINGS_INFO.THINGS things = THINGS_INFO.getThingsType(modelId);
 		String[] splitStr = status.split("\\/");
 		String[] splitStr = status.split("\\/");
+		
+		if(splitStr[0].equals("null")) return state;
+		
 		int temp1, temp2 = 0;
 		int temp1, temp2 = 0;
 		switch(things.getEndPoint()) {
 		switch(things.getEndPoint()) {
 		case THINGS_INFO.THINGS_ELECPLUG:
 		case THINGS_INFO.THINGS_ELECPLUG:
@@ -965,7 +1561,7 @@ public class KakaoMsgHandler {
 			if(splitStr.length == 1) break;
 			if(splitStr.length == 1) break;
 			
 			
 			if(splitStr[1] != null && !splitStr[1].equals("null")) {
 			if(splitStr[1] != null && !splitStr[1].equals("null")) {
-				temp1 = Integer.parseInt(splitStr[1]) * 10;
+				temp1 = Integer.parseInt(splitStr[1]);
 				state.put("brightness", temp1);
 				state.put("brightness", temp1);
 			}
 			}
 			
 			
@@ -976,10 +1572,13 @@ public class KakaoMsgHandler {
 			}
 			}
 			break;
 			break;
 		case THINGS_INFO.THINGS_TEMPER:
 		case THINGS_INFO.THINGS_TEMPER:
+			Double targetTemp = Double.parseDouble(splitStr[1]);
+			Double currTemp = Double.parseDouble(splitStr[2]);
 			
 			
 			state.put("power", splitStr[0]);
 			state.put("power", splitStr[0]);
-			state.put("targetSetpoint", Double.parseDouble(splitStr[1]));
-			state.put("currentTemperature", Double.parseDouble(splitStr[2]));
+			state.put("targetSetpoint", Double.valueOf(targetTemp.intValue()));
+			state.put("currentTemperature", Double.valueOf(currTemp.intValue()));
+			
 			
 			
 			break;
 			break;
 		case THINGS_INFO.THINGS_GAS:
 		case THINGS_INFO.THINGS_GAS:
@@ -1000,6 +1599,53 @@ public class KakaoMsgHandler {
 		case THINGS_INFO.THINGS_ELEVATOR:
 		case THINGS_INFO.THINGS_ELEVATOR:
 			state.put("callDirection", "down");
 			state.put("callDirection", "down");
 			break;
 			break;
+		case THINGS_INFO.THINGS_AIR_MONITOR:
+			
+			int val_co2 = (int) Double.parseDouble(splitStr[0]);
+			int val_pm10 = (int) Double.parseDouble(splitStr[1]);
+			int val_pm2_5 = (int) Double.parseDouble(splitStr[2]);
+			
+			int qual_co2 = getAirQuaility("co2", val_co2);
+			int qual_pm10 = getAirQuaility("pm10", val_pm10);
+			int qual_pm2_5 = getAirQuaility("pm2_5", val_pm2_5);
+			
+			String[] arr_quality = {"good", "moderate", "unhealthy", "very_unhealth"};
+			int avg_qual = (qual_co2 + qual_pm10 + qual_pm2_5)/3;
+			String qualityIndex = arr_quality[avg_qual];
+			
+			state.put("co2", val_co2);
+			state.put("pm10", val_pm10);
+			state.put("pm2_5", val_pm2_5);
+			state.put("qualityIndex", qualityIndex);
+			break;
+		case THINGS_INFO.THINGS_AIRCON:
+			logger.info("[에어컨 상태]-"+ status);
+			
+			String power = splitStr[0].equalsIgnoreCase("removeheat")?"off":splitStr[0];
+			int set_temp = splitStr.length > 1 ? Integer.parseInt(splitStr[1]):0;
+			int cur_temp = splitStr.length > 2 ?Integer.parseInt(splitStr[2]):0;
+			String mode = splitStr.length > 3 ? splitStr[3]:null;
+			String airFlow = splitStr.length > 4 ? splitStr[4]:null;
+			
+			state.put("power", power);
+			state.put("targetSetpoint", set_temp);
+			state.put("currentTemperature", cur_temp);
+			
+			if(mode != null) {
+				if(mode.equals("auto")) state.put("mode", "auto");
+				else if(mode.equals("cool")) state.put("mode", "cool");
+				else if(mode.equals("heat")) state.put("mode", "heat");
+				else if(mode.equals("dehum")) state.put("mode", "dehumidify");
+				else if(mode.equals("fan")) state.put("mode", "fan_only");
+			}
+			
+			if(airFlow != null) {
+				if(airFlow.equals("auto")) state.put("fanSpeed", "auto");
+				else if(airFlow.equals("low")) state.put("fanSpeed", "low");
+				else if(airFlow.equals("mid")) state.put("fanSpeed", "middle");
+				else if(airFlow.equals("high")) state.put("fanSpeed", "high");
+			}
+			break;
 		}
 		}
 		
 		
 		return state;
 		return state;

+ 321 - 30
src/main/java/com/icontrols/kakao/socket/wallpad/ThreadServerHandler.java

@@ -24,14 +24,18 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.Node;
 
 
 import com.icontrols.kakao.common.KAKAO_RESULT;
 import com.icontrols.kakao.common.KAKAO_RESULT;
-import com.icontrols.kakao.common.SERVERINFO;
 import com.icontrols.kakao.common.utils.BeanUtils;
 import com.icontrols.kakao.common.utils.BeanUtils;
+import com.icontrols.kakao.common.utils.CommonUtil;
+import com.icontrols.kakao.common.utils.JWTUtils2;
 import com.icontrols.kakao.common.utils.XmlUtil;
 import com.icontrols.kakao.common.utils.XmlUtil;
 import com.icontrols.kakao.config.AsyncConfig;
 import com.icontrols.kakao.config.AsyncConfig;
 import com.icontrols.kakao.entity.DonghoInfo;
 import com.icontrols.kakao.entity.DonghoInfo;
+import com.icontrols.kakao.entity.ParcelInfo;
 import com.icontrols.kakao.entity.UserInfo;
 import com.icontrols.kakao.entity.UserInfo;
 import com.icontrols.kakao.repo.DonghoInfoRepository;
 import com.icontrols.kakao.repo.DonghoInfoRepository;
+import com.icontrols.kakao.repo.ParcelInfoRepository;
 import com.icontrols.kakao.repo.UserInfoRepository;
 import com.icontrols.kakao.repo.UserInfoRepository;
+import com.icontrols.kakao.service.AccessTokenService;
 
 
 /**
 /**
  * 
  * 
@@ -165,12 +169,20 @@ public class ThreadServerHandler extends Thread {
 				String requestCmd = XmlUtil.getAttributeWithItem(service, "name");
 				String requestCmd = XmlUtil.getAttributeWithItem(service, "name");
 				
 				
 				logger.debug("[ThreadServerHandler]-requestCmd : " + requestCmd);
 				logger.debug("[ThreadServerHandler]-requestCmd : " + requestCmd);
-//				if (requestCmd.equalsIgnoreCase("msg_device_event")) { // 상태변경이벤트인경우에 처리
+				if (requestCmd.equalsIgnoreCase("msg_device_event")) { // 상태변경이벤트인경우에 처리
 					logger.debug("[ThreadServerHandler]-strRcvMSG : " + strRcvMSG);
 					logger.debug("[ThreadServerHandler]-strRcvMSG : " + strRcvMSG);
 					// 빈번하게 실행 될 가능성이 높다.
 					// 빈번하게 실행 될 가능성이 높다.
 					DeviceEventMsgHandler emh = new DeviceEventMsgHandler(doc, requestCmd);
 					DeviceEventMsgHandler emh = new DeviceEventMsgHandler(doc, requestCmd);
 					emh.start();
 					emh.start();
-//				}
+				}else if (requestCmd.equalsIgnoreCase("msg_box_event")) { // 택배 이벤트인 경우에 처리
+					logger.info("[ParcelEventMsgHandler]-[실행]");
+					ParcelEventMsgHandler pmh = new ParcelEventMsgHandler(doc);
+					pmh.start();
+				}else if (requestCmd.equalsIgnoreCase("msg_dustinfo_event")) { // 공기질 이벤트인 경우에 처리
+					logger.info("[DustEventMsgHandler]-[실행]");
+					DustEventMsgHandler dmh = new DustEventMsgHandler(doc);
+					dmh.start();
+				}
 			}
 			}
 		} catch (Exception e) {
 		} catch (Exception e) {
 			logger.error("message : " + e);
 			logger.error("message : " + e);
@@ -218,8 +230,6 @@ public class ThreadServerHandler extends Thread {
 				String dong = dongho.substring(0, idxdong).trim();
 				String dong = dongho.substring(0, idxdong).trim();
 				String ho = dongho.substring(idxdong + 1, idxho).trim();
 				String ho = dongho.substring(idxdong + 1, idxho).trim();
 				
 				
-				
-
 				Node service = (Node) xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
 				Node service = (Node) xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
 				logger.debug(service.getAttributes().getNamedItem("type").getTextContent());
 				logger.debug(service.getAttributes().getNamedItem("type").getTextContent());
 
 
@@ -266,38 +276,48 @@ public class ThreadServerHandler extends Thread {
 						.forEach(userInfo -> {
 						.forEach(userInfo -> {
 						try {
 						try {
 							UserInfoRepository uRepo = (UserInfoRepository)BeanUtils.getBean("userInfoRepository");
 							UserInfoRepository uRepo = (UserInfoRepository)BeanUtils.getBean("userInfoRepository");
-							Map<String, Object> sendMap = null;
-							if(status.equalsIgnoreCase("unsubscription")) {
-								// 구독해지 처리.
+							//사용자 access token이 유효한지 판단.
+							int token_vali = JWTUtils2.validateToken(userInfo.getAccessToken());
+							if(token_vali != KAKAO_RESULT.SUCCESS) {
+								// 사용자 access token이 만료 됐으면 디비에 Update하자.
 								userInfo.setDeleteFlag("Y");
 								userInfo.setDeleteFlag("Y");
-								userInfo.setDeleteType("unsubscription");
+								userInfo.setDeleteType("expiredToken");
 								uRepo.save(userInfo);
 								uRepo.save(userInfo);
-								logger.info("[해지처리]-["+userInfo.getDonghoInfo().getDonghoId()+"]-[userID = " + userInfo.getId()+"]");
-								// 삭제 이벤트 전송해야...
-								sendMap = kakaoMsgHandler.setKDeleteIdsMsg(dong, ho, userInfo);
-								logger.info("[UNSUBSCRIPTION] - [sendMap] - " + sendMap.toString());
-								
 							}else {
 							}else {
-								if(state.containsKey("colorTemperature") && state.containsKey("brightness")) {
-									body.put("colorDimmer_"+endpointId, state);
-								}else if(state.containsKey("brightness")) {
-									body.put("dimmer_"+endpointId, state);	
+								Map<String, Object> sendMap = null;
+								if(status.equalsIgnoreCase("unsubscription")) {
+									// 구독해지 처리.
+									userInfo.setDeleteFlag("Y");
+									userInfo.setDeleteType("unsubscription");
+									uRepo.save(userInfo);
+									logger.info("[해지처리]-["+userInfo.getDonghoInfo().getDonghoId()+"]-[userID = " + userInfo.getId()+"]");
+									// 삭제 이벤트 전송해야...
+									sendMap = kakaoMsgHandler.setKDeleteIdsMsg(dong, ho, userInfo);
+									logger.info("[UNSUBSCRIPTION] - [sendMap] - " + sendMap.toString());
 								}else {
 								}else {
-									body.put(endpointId, state);
+									if(state.isEmpty()) return;
+									
+									if(state.containsKey("colorTemperature") && state.containsKey("brightness")) {
+										body.put("colorDimmer_"+endpointId, state);
+									}else if(state.containsKey("brightness")) {
+										body.put("dimmer_"+endpointId, state);	
+									}else {
+										body.put(endpointId, state);
+									}
+									
+									logger.debug("body --> " + body.toString());
+									
+									sendMap = kakaoMsgHandler.setKakaoEventMsg(userInfo, body);
 								}
 								}
+								logger.debug("sendMap --> " + sendMap.toString());
 								
 								
-								logger.debug("body --> " + body.toString());
-								
-								sendMap = kakaoMsgHandler.setKakaoEventMsg(userInfo, body);
-							}
-							logger.debug("sendMap --> " + sendMap.toString());
-							
-							while(true) {
-								if(asyncConfig.isEventTaskExecute()) {
-									fList.add(kakaoEventHandler.sendEventAsync(userInfo, sendMap));
-									break;
+								while(true) {
+									if(asyncConfig.isEventTaskExecute()) {
+										fList.add(kakaoEventHandler.sendEventAsync(userInfo, sendMap));
+										break;
+									}
+									Thread.sleep(1000);
 								}
 								}
-								Thread.sleep(1000);
 							}
 							}
 						}catch(Exception e) {
 						}catch(Exception e) {
 							logger.error("[ERROR - ASYNC EVENT SEND] - " + e.getMessage());
 							logger.error("[ERROR - ASYNC EVENT SEND] - " + e.getMessage());
@@ -331,4 +351,275 @@ public class ThreadServerHandler extends Thread {
 			}
 			}
 		}
 		}
 	}
 	}
+	
+	// 클라이언트로 데이터를 전송할 스레드 클래스
+	class ParcelEventMsgHandler extends Thread {
+		// 멤버변수
+		private Document doc;
+		
+		// 생성자
+		public ParcelEventMsgHandler(Document doc) {
+			this.doc = doc;
+		}
+
+		public void run() {
+			try {
+				DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
+				ParcelInfoRepository parcelRepo = (ParcelInfoRepository)BeanUtils.getBean("parcelInfoRepository");
+				
+				KakaoMsgHandler kakaoMsgHandler = (KakaoMsgHandler)BeanUtils.getBean("kakaoMsgHandler");
+				Map <String, Object> body = new HashMap<String, Object>();
+				
+				// xpath 생성
+				XPath xpath = XPathFactory.newInstance().newXPath();
+
+				// id 가 c2 인 Node의 val attribute 값 가져오기
+				Node iMap = (Node) xpath.evaluate("//imap", doc, XPathConstants.NODE);
+				logger.debug("[ParcelEventMsgHandler] - address : " + iMap.getAttributes().getNamedItem("address").getTextContent());
+				logger.debug("[ParcelEventMsgHandler] - sender : "  + iMap.getAttributes().getNamedItem("sender").getTextContent());
+
+
+				String dongho = XmlUtil.getAttributeWithItem(iMap, "sender");
+				String searchDong = (dongho.indexOf("d") >= 0)?"d":"동";
+				String searchHo = (dongho.indexOf("d") >= 0)?"h":"호";
+
+				int idxdong = dongho.indexOf(searchDong);
+				int idxho = dongho.indexOf(searchHo);
+
+				String dong = dongho.substring(0, idxdong).trim();
+				String ho = dongho.substring(idxdong + 1, idxho).trim();
+				
+				Node service = (Node) xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+				logger.debug(service.getAttributes().getNamedItem("type").getTextContent());
+				int quantity = (int)((double) xpath.evaluate("//imap/service/quantity", doc, XPathConstants.NUMBER));
+				String moveInfo = (String) xpath.evaluate("//imap/service/move_info", doc, XPathConstants.STRING);
+				String reportTime = (String) xpath.evaluate("//imap/service/report_time", doc, XPathConstants.STRING);
+				//"2019-04-26T16:20:55Z" 형식을 "2019-07-09T13:25:31Z" 형식으로 변환해야함.
+				String lastDeliveredTime = CommonUtil.convertTimeToKakaoTime(reportTime);
+				logger.info("[ParcelEvent]-[dong="+dong+"]-[ho="+ ho +"]-[move_info="+ moveInfo + "]-[quantity="+ quantity + "]-[reportTime= "+reportTime+"]-[lastDeliveredTime= "+lastDeliveredTime+"]");
+				
+				List<ParcelInfo> list = parcelRepo.findByDonghoId(dong+"/"+ho);
+				Map<String, Object> stateMap = new HashMap<>();
+				
+				if(list.size() == 1) {
+					ParcelInfo parcelObj = list.get(0);
+					
+					parcelObj.setQuantity(quantity);
+					parcelObj.setLstUptEvt(moveInfo);
+					parcelObj.setLstUptTime(lastDeliveredTime);
+					List<Object> parcelList = new ArrayList<>();
+					
+					for(int i=0; i < parcelObj.getQuantity(); i++) {
+						Map<String, Object> _obj = new HashMap<>();
+						_obj.put("name", "택배 보관함");
+						_obj.put("lastDeliveredTime", parcelObj.getLstUptTime());
+						parcelList.add(_obj);
+					}
+					
+					stateMap.put("parcelLocations", parcelList);
+					
+					parcelRepo.save(parcelObj);
+				}
+				logger.info("[ParcelEventMsgHandler]-[stateMap]-"+ stateMap.toString());
+				
+				AsyncConfig asyncConfig = (AsyncConfig)BeanUtils.getBean("asyncConfig");
+				
+				//dong ho 가  같은 유저 모두 조회
+				List<DonghoInfo> dList = repo.findByDongAndHo(dong, ho);
+				
+				if(dList.size() == 0) {
+					logger.info("[EVENT] - 해동 동 호 인원 없음 프로세스 종료");
+					return;
+				}
+				KakaoEventHandler kakaoEventHandler = (KakaoEventHandler)BeanUtils.getBean("kakaoEventHandler");
+				
+				List<CompletableFuture<Map<String, Object>>> fList = new ArrayList<>();
+				List<UserInfo> userSet = dList.get(0).getUserInfos();
+				
+				userSet.stream()
+					.filter(userInfo -> userInfo.getDeleteFlag().equals("N"))
+					.forEach(userInfo -> {
+					try {
+						UserInfoRepository uRepo = (UserInfoRepository)BeanUtils.getBean("userInfoRepository");
+						Map<String, Object> sendMap = null;
+						if(stateMap.isEmpty()) return;
+						
+						body.put("parcel", stateMap);
+						
+						logger.debug("body --> " + body.toString());
+						sendMap = kakaoMsgHandler.setKakaoEventMsg(userInfo, body);
+						logger.debug("sendMap --> " + sendMap.toString());
+						
+						while(true) {
+							if(asyncConfig.isEventTaskExecute()) {
+								fList.add(kakaoEventHandler.sendEventAsync(userInfo, sendMap));
+								break;
+							}
+							Thread.sleep(1000);
+						}
+					}catch(Exception e) {
+						logger.error("[ERROR - ASYNC EVENT SEND] - " + e.getMessage());
+						e.printStackTrace();
+					}
+				});
+				
+				CompletableFuture.allOf(fList.toArray(new CompletableFuture[fList.size()])).join();
+				// 이벤트 전송이 실패했는지 성공했는지..
+				fList.stream().filter(future -> future != null).forEach(future -> { 
+					try {
+						Map<String, Object> eventResult = future.get();
+						
+						int code = (int)eventResult.get("result");
+						String message = (String)eventResult.get("message");
+						
+						if(code == KAKAO_RESULT.SUCCESS) {
+							logger.debug("[EVENT SUCCESS] - " + message);
+						} else {
+							logger.error("[EVENT ERROR] - [CODE-" + code + "]" + message);
+							
+						}							
+					}catch(Exception e) {
+						logger.error("[ERROR - future get] - " + e.getMessage());
+						e.printStackTrace();
+					}
+				});
+			} catch (Exception e) {
+				logger.error("message : " + e);
+				e.printStackTrace();
+			}
+		}
+	}
+	
+	// 클라이언트로 데이터를 전송할 스레드 클래스
+		class DustEventMsgHandler extends Thread {
+			// 멤버변수
+			private Document doc;
+			
+			// 생성자
+			public DustEventMsgHandler(Document doc) {
+				this.doc = doc;
+			}
+
+			public void run() {
+				try {
+					DonghoInfoRepository repo = (DonghoInfoRepository)BeanUtils.getBean("donghoInfoRepository");
+					
+					KakaoMsgHandler kakaoMsgHandler = (KakaoMsgHandler)BeanUtils.getBean("kakaoMsgHandler");
+					Map <String, Object> body = new HashMap<String, Object>();
+					
+					// xpath 생성
+					XPath xpath = XPathFactory.newInstance().newXPath();
+
+					// id 가 c2 인 Node의 val attribute 값 가져오기
+					Node iMap = (Node) xpath.evaluate("//imap", doc, XPathConstants.NODE);
+					logger.debug("[DustEventMsgHandler] - address : " + iMap.getAttributes().getNamedItem("address").getTextContent());
+					logger.debug("[DustEventMsgHandler] - sender : "  + iMap.getAttributes().getNamedItem("sender").getTextContent());
+
+
+					String dongho = XmlUtil.getAttributeWithItem(iMap, "sender");
+					String searchDong = (dongho.indexOf("d") >= 0)?"d":"동";
+					String searchHo = (dongho.indexOf("d") >= 0)?"h":"호";
+
+					int idxdong = dongho.indexOf(searchDong);
+					int idxho = dongho.indexOf(searchHo);
+
+					String dong = dongho.substring(0, idxdong).trim();
+					String ho = dongho.substring(idxdong + 1, idxho).trim();
+					
+					
+					String model_id = (String) xpath.evaluate("//imap/service/model_id ", doc, XPathConstants.STRING);
+					String dev_num = (String) xpath.evaluate("//imap/service/dev_num", doc, XPathConstants.STRING);
+					String endpointId = model_id + "_" + dev_num;
+					
+					Node service = (Node) xpath.evaluate("//imap/service", doc, XPathConstants.NODE);
+					logger.debug(service.getAttributes().getNamedItem("type").getTextContent());
+					
+					Map<String, Object> stateMap = new HashMap<>();
+					
+					int val_co2 = (int)(double) xpath.evaluate("//imap/service/co2", doc, XPathConstants.NUMBER);
+					int val_pm10 = (int)(double) xpath.evaluate("//imap/service/fine_dust", doc, XPathConstants.NUMBER);
+					int val_pm2_5 = (int)(double) xpath.evaluate("//imap/service/ultrafine_dust", doc, XPathConstants.NUMBER);
+					
+					int qual_co2 = CommonUtil.getAirQuaility("co2", val_co2);
+					int qual_pm10 = CommonUtil.getAirQuaility("pm10", val_pm10);
+					int qual_pm2_5 = CommonUtil.getAirQuaility("pm2_5", val_pm2_5);
+					
+					String[] arr_quality = {"good", "moderate", "unhealthy", "very_unhealth"};
+					int avg_qual = (qual_co2 + qual_pm10 + qual_pm2_5)/3;
+					String qualityIndex = arr_quality[avg_qual];
+					
+					stateMap.put("co2", val_co2);
+					stateMap.put("pm10", val_pm10);
+					stateMap.put("pm2_5", val_pm2_5);
+					stateMap.put("qualityIndex", qualityIndex);
+					
+					logger.info("[DustEventMsgHandler]-[stateMap]-"+ stateMap.toString());
+					AsyncConfig asyncConfig = (AsyncConfig)BeanUtils.getBean("asyncConfig");
+					
+					//dong ho 가  같은 유저 모두 조회
+					List<DonghoInfo> dList = repo.findByDongAndHo(dong, ho);
+					
+					if(dList.size() == 0) {
+						logger.info("[EVENT] - 해동 동 호 인원 없음 프로세스 종료");
+						return;
+					}
+					
+					KakaoEventHandler kakaoEventHandler = (KakaoEventHandler)BeanUtils.getBean("kakaoEventHandler");
+					
+					List<CompletableFuture<Map<String, Object>>> fList = new ArrayList<>();
+					List<UserInfo> userSet = dList.get(0).getUserInfos();
+					
+					userSet.stream()
+						.filter(userInfo -> userInfo.getDeleteFlag().equals("N"))
+						.forEach(userInfo -> {
+							try {
+								UserInfoRepository uRepo = (UserInfoRepository)BeanUtils.getBean("userInfoRepository");
+								Map<String, Object> sendMap = null;
+								if(stateMap.isEmpty()) return;
+								
+								body.put(endpointId, stateMap);
+								
+								logger.debug("body --> " + body.toString());
+								sendMap = kakaoMsgHandler.setKakaoEventMsg(userInfo, body);
+								logger.debug("sendMap --> " + sendMap.toString());
+								
+								while(true) {
+									if(asyncConfig.isEventTaskExecute()) {
+										fList.add(kakaoEventHandler.sendEventAsync(userInfo, sendMap));
+										break;
+									}
+									Thread.sleep(1000);
+								}
+							}catch(Exception e) {
+								logger.error("[ERROR - ASYNC EVENT SEND] - " + e.getMessage());
+								e.printStackTrace();
+							}
+						});
+					
+					CompletableFuture.allOf(fList.toArray(new CompletableFuture[fList.size()])).join();
+					// 이벤트 전송이 실패했는지 성공했는지..
+					fList.stream().filter(future -> future != null).forEach(future -> { 
+						try {
+							Map<String, Object> eventResult = future.get();
+							
+							int code = (int)eventResult.get("result");
+							String message = (String)eventResult.get("message");
+							
+							if(code == KAKAO_RESULT.SUCCESS) {
+								logger.debug("[EVENT SUCCESS] - " + message);
+							} else {
+								logger.error("[EVENT ERROR] - [CODE-" + code + "]" + message);
+								
+							}							
+						}catch(Exception e) {
+							logger.error("[ERROR - future get] - " + e.getMessage());
+							e.printStackTrace();
+						}
+					});
+				} catch (Exception e) {
+					logger.error("message : " + e);
+					e.printStackTrace();
+				}
+			}
+		}
 }
 }

+ 73 - 0
src/main/java/com/icontrols/kakao/socket/wallpad/WallpadMsgSender.java

@@ -329,4 +329,77 @@ public class WallpadMsgSender {
 		
 		
 		return bRet == -1 ? null : imapSocket.getDocument();
 		return bRet == -1 ? null : imapSocket.getDocument();
 	}
 	}
+	
+	/**
+	 * 해당 세대의 주차위치 정보를 조회합니.
+	 * @param dong
+	 * @param ho
+	 * @return
+	 */
+	public Document remoteGetParkingList(int dong, int ho) {
+		Document doc = null;
+		try {
+			iMapRequestSync imapSocket = new iMapRequestSync(
+					IPGenerator.getIPFromDongHo(dong, ho),
+					SERVERINFO.clientNewPort);
+			
+			String xmlData = null;
+			StringBuffer sb = new StringBuffer();
+			
+			sb.append(XMLGenerator.MakeXMLHeader(dong, ho,REMOTE_KIND.msg_car_parking_list.getContents()))
+				.append(XMLGenerator.MakeXMLTail());
+		
+			xmlData = sb.toString();
+			
+			int bRet = imapSocket.doWork(xmlData);
+			
+			if(bRet < 0) {
+				logger.error("[remoteDeviceList ERROR] - document null");
+				return null;
+			}
+			
+			return imapSocket.getDocument();
+		}catch(Exception e) {
+			logger.error("[remoteDeviceList ERROR] - " + e.getMessage());
+			return null;
+		}
+	}
+	
+	/**
+	 * 에어컨의 속성을 조회합니다.
+	 * @param dong
+	 * @param ho
+	 * @return
+	 */
+	public Document remoteGetAirconAttributes(int dong, int ho) {
+		Document doc = null;
+		try {
+			iMapRequestSync imapSocket = new iMapRequestSync(
+					IPGenerator.getIPFromDongHo(dong, ho),
+					SERVERINFO.clientNewPort);
+			
+			String xmlData = null;
+			StringBuffer sb = new StringBuffer();
+			
+			sb.append(XMLGenerator.MakeXMLHeader(dong, ho,REMOTE_KIND.msg_aircon_attributes.getContents()))
+				.append(XMLGenerator.MakeXMLTail());
+		
+			xmlData = sb.toString();
+			
+			int bRet = imapSocket.doWork(xmlData);
+			
+			if(bRet < 0) {
+				logger.error("[remoteGetAirconAttributes ERROR] - document null");
+				return null;
+			}
+			doc = imapSocket.getDocument();
+			if(doc == null) {
+				logger.error("doc is null - ");
+			}
+			return imapSocket.getDocument();
+		}catch(Exception e) {
+			logger.error("[remoteGetAirconAttributes ERROR] - " + e.getMessage());
+			return null;
+		}
+	}
 }
 }

+ 6 - 7
src/main/resources/application.properties

@@ -1,17 +1,16 @@
 logging.config=classpath:logback-spring.xml
 logging.config=classpath:logback-spring.xml
 
 
-## local 황의현 개발 디비
+
 #spring.datasource.url=jdbc:mariadb://localhost:33060/platform_danzi?useUnicode=true&charaterEncoding=utf-8
 #spring.datasource.url=jdbc:mariadb://localhost:33060/platform_danzi?useUnicode=true&charaterEncoding=utf-8
 #spring.datasource.username=root
 #spring.datasource.username=root
 #spring.datasource.password=pangpang
 #spring.datasource.password=pangpang
 
 
-## 배포용-테스트 단지서버
-#spring.datasource.url=jdbc:mariadb://192.168.100.155:8005/platform_danzi?useUnicode=true&charaterEncoding=utf-8
-spring.datasource.url=jdbc:mariadb://192.168.100.155:8005/platform_kakao?useUnicode=true&charaterEncoding=utf-8
+## 기술 연구소 단지서버 
+#spring.datasource.url=jdbc:mariadb://61.33.215.54:8005/platform_kakao?useUnicode=true&charaterEncoding=utf-8
+
+## local (배포용)
+spring.datasource.url=jdbc:mariadb://221.163.243.3:8005/platform_kakao?useUnicode=true&charaterEncoding=utf-8
 
 
-## 배포용-카카오데모서버
-##spring.datasource.url=jdbc:mariadb://localhost:8005/platform_danzi?useUnicode=true&charaterEncoding=utf-8 
-#spring.datasource.url=jdbc:mariadb://localhost:8005/platform_kakao?useUnicode=true&charaterEncoding=utf-8
 spring.datasource.username=valley
 spring.datasource.username=valley
 spring.datasource.password=valley123
 spring.datasource.password=valley123