삼성 에어모니터 ACM-B1MOS 로컬 Home Assistant 연동
1. 목적과 현재 상태
삼성 에어모니터 스탠다드 ACM-B1MOS의 센서 값을 SmartThings 클라우드를 거치지 않고 로컬 MQTT와 Home Assistant에서 사용하기 위한 작업 기록이다.
현재 다음 항목까지 확인했다.
장치 IP:
192.168.1.51장치 모델: Samsung ACM-B1MOS
MQTT 서버 도메인:
mqtt.ko.cleargrass.comHTTPS 서버 도메인:
sansa.cleargrass.comMQTT 프로토콜: MQTT 3.1.1, 평문 TCP
1883MQTT command topic:
sansa/command/7EF6FAA4016711EBA78F00163E000886MQTT data topic:
sansa/data/7EF6FAA4016711EBA78F00163E000886MQTT 명령으로 로컬 센서 데이터 발행 확인 (
up_itvl: 10의 정확한 10초 고정 주기 동작은 미확인)Base64로 감싼 JSON 센서 데이터 디코딩 확인
Home Assistant MQTT 센서 구성 확인
이 문서에서는 Mosquitto 설치나 일반적인 브로커 설정 방법은 다루지 않는다. 이미 사용할 수 있는 MQTT 브로커가 있다는 전제에서 DNS 우회, 인증 선택, 패킷 캡처, Home Assistant 등록만 설명한다.
2. 확인된 네트워크 동작
장치를 완전히 부팅하면 다음 DNS 조회와 연결이 발생한다.
mqtt.ko.cleargrass.com → MQTT TCP 1883
sansa.cleargrass.com → HTTPS TCP 443
www.baidu.com → 인터넷 연결 확인으로 추정
캡처 당시 외부 주소는 다음과 같았다. 외부 IP는 바뀔 수 있으므로 고정값으로 사용하면 안 된다.
mqtt.ko.cleargrass.com → 39.106.170.175
sansa.cleargrass.com → 161.117.191.11
장치는 공유기 192.168.1.1을 DNS 서버로 사용했다. 따라서 공유기 DNS에서 MQTT 도메인만 내부 브로커로 돌리면 된다.
mqtt.ko.cleargrass.com → 내부 MQTT 브로커 IP
sansa.cleargrass.com은 HTTPS이므로 처음에는 우회하지 않는다. 이 도메인까지 내부 서버로 변경하면 장치의 TLS 인증서 검증 때문에 통신이 실패할 수 있다.
3. MQTT 연결 방식 선택
옵션 1: 익명 MQTT 브로커 사용
가장 빠르고 단순한 방법이다.
ACM-B1MOS
→ mqtt.ko.cleargrass.com DNS 우회
→ 익명 접속 허용 MQTT 브로커:1883
→ Home Assistant
브로커가 익명 접속을 허용하면 장치의 원래 MQTT ID/PW를 알아낼 필요가 없다. 장치는 CONNECT 패킷에 자체 사용자명을 보내더라도 익명 접속을 허용하는 브로커에 정상 연결할 수 있다.
이 방식은 다음 경우에 적합하다.
- MQTT 브로커가 IoT 전용 내부망에 있음
- 외부에서 TCP 1883에 접근할 수 없음
- 장치 인증정보를 추출하지 않고 빠르게 연동하려는 경우
- 분석용 또는 전용 브로커를 별도 IP에서 운영하는 경우
익명 브로커는 인터넷에 노출하지 않는다. 최소한 공유기 방화벽에서 LAN 내부 접속만 허용해야 한다.
옵션 2: 인증 MQTT 브로커 사용
기존 Home Assistant Mosquitto처럼 사용자명과 비밀번호가 필수인 브로커를 직접 사용하는 방식이다.
ACM-B1MOS
→ mqtt.ko.cleargrass.com DNS 우회
→ 기존 HA MQTT 브로커:1883
→ Home Assistant
이 경우 장치가 보내는 원래 MQTT 사용자명과 비밀번호를 캡처하고, 동일한 인증정보를 대상 브로커에 등록해야 한다.
장점은 다음과 같다.
- 기존 Home Assistant MQTT 브로커를 그대로 사용
- 별도 분석 브로커와 MQTT bridge가 불필요
- 동일 IP의 TCP 1883 포트 충돌이 없음
- 인증되지 않은 LAN 클라이언트의 접속을 차단 가능
장치 인증정보를 브로커에 등록하는 구체적인 방법은 브로커 배포 방식마다 다르므로 이 문서에서는 다루지 않는다.
4. Synology RT2600ac에서 DNS 우회 확인
RT2600ac SRM 1.3의 기본 LAN 인터페이스는 일반적으로 lbr0이다. 실제 인터페이스를 먼저 확인한다.
ip route | grep 192.168.1
공유기 DNS에 다음 로컬 레코드를 등록한다.
mqtt.ko.cleargrass.com → MQTT_BROKER_IP
예를 들어 내부 브로커가 192.168.1.13이면 다음 결과가 나와야 한다.
nslookup mqtt.ko.cleargrass.com 192.168.1.1
Name: mqtt.ko.cleargrass.com
Address: 192.168.1.13
DNS와 MQTT 접속을 동시에 확인하려면 공유기에서 다음을 실행한다.
tcpdump -ni lbr0 -vv \
'host 192.168.1.51 and (port 53 or tcp port 1883)'
정상 동작 예:
192.168.1.51 → 192.168.1.1:53
192.168.1.1 → 192.168.1.51: mqtt.ko.cleargrass.com A 192.168.1.13
192.168.1.51 → 192.168.1.13:1883 SYN
tcpdump의 다음 메시지는 공유기의 체크섬 오프로딩 때문에 나타날 수 있으며 DNS 오류를 의미하지 않는다.
bad udp cksum
5. 장치를 제대로 재부팅하는 방법
ACM-B1MOS에는 내장 배터리가 있다. USB-C 전원만 분리해도 장치가 계속 실행되므로 DNS 캐시와 MQTT 세션이 유지될 수 있다.
DNS 변경 후에는 다음 절차로 완전히 재부팅한다.
- 장치 메뉴 또는 전원 버튼으로 화면이 완전히 꺼질 때까지 종료한다.
- 10초 정도 기다린다.
- 장치를 다시 켠다.
- 공유기의 tcpdump와 MQTT 로그에서 재접속을 확인한다.
MQTT 로그에는 장치 IP가 명확히 나타나야 한다.
New connection from 192.168.1.51
New client connected ... as 7EF6FAA4016711EBA78F00163E000886
127.0.0.1에서 접속한 auto-... 클라이언트는 로컬에서 실행한 mosquitto_sub이며 ACM-B1MOS가 아니다.
6. 옵션 2용 MQTT ID/PW 추출
옵션 1의 익명 MQTT를 사용할 때는 이 과정이 필요 없다.
MQTT TCP 1883은 암호화되지 않았으므로 CONNECT 패킷 안의 Client ID, 사용자명, 비밀번호를 PCAP에서 확인할 수 있다.
6.1 공유기에서 CONNECT 패킷 캡처
tcpdump -ni lbr0 -s 0 \
'host 192.168.1.51 and tcp port 1883' \
-w /tmp/acm-mqtt-auth.pcap
캡처를 실행한 상태에서 장치를 완전히 재부팅한다. MQTT 접속이 끝난 뒤 Ctrl+C로 종료한다.
구형 SRM에서 PC로 가져올 때 최신 SCP의 SFTP 모드가 동작하지 않으면 기존 SCP 프로토콜을 강제한다.
scp -O root@192.168.1.1:/tmp/acm-mqtt-auth.pcap .
SSH RSA 호환 오류가 발생하면 필요한 옵션만 추가한다.
scp -O `
-o HostKeyAlgorithms=+ssh-rsa `
-o PubkeyAcceptedAlgorithms=+ssh-rsa `
root@192.168.1.1:/tmp/acm-mqtt-auth.pcap .
6.2 Wireshark에서 확인
표시 필터:
mqtt.msgtype == 1
MQTT CONNECT 패킷에서 다음 필드를 확인한다.
Client ID
User Name
Password
6.3 Windows tshark로 추출
& "C:\Program Files\Wireshark\tshark.exe" `
-r ".\acm-mqtt-auth.pcap" `
-Y "mqtt.msgtype == 1" `
-T fields `
-E "header=y" `
-E "quote=d" `
-e mqtt.clientid `
-e mqtt.username `
-e mqtt.passwd
출력된 사용자명과 비밀번호를 인증 MQTT 브로커에 동일하게 등록한다. 비밀번호와 인증 PCAP은 공개 저장소나 채팅에 올리지 않는다.
7. 로컬 MQTT 통신 활성화
장치는 연결 직후 다음 command topic을 구독한다.
sansa/command/7EF6FAA4016711EBA78F00163E000886
서버가 아무 명령도 보내지 않으면 장치는 약 1초 후 연결을 종료할 수 있다. 다음 메시지를 retained로 발행하면 센서 데이터 보고를 요청할 수 있다.
{"type":"12","up_itvl":"10","duration":"86400"}
동작 여부 주의: 이 명령으로 데이터 발행이 시작되는 것은 확인했지만, ACM-B1MOS가
up_itvl: 10을 정확한 10초 고정 주기로 지키는지는 확인되지 않았다. 실제 관찰에서는 메시지가 약 20초~1분 간격으로 불규칙하게 도착했다. 온도·습도 묶음과 공기질 묶음의 측정 및 발행 시점도 서로 다르므로10은 현재 10초 보고 요청값으로 취급한다.
type: 12와up_itvl: 10은 Qingping 공식 MQTT 문서의 임시 보고 예제에서 가져왔다. 공식 예제의duration은60초다.duration: 86400은 패킷 캡처에서 추출한 값이 아니라, Home Assistant 사용자 설정 사례처럼 명령을 24시간 유지하기 위해 확장한 값이다. 따라서 위 전체 명령은 ACM-B1MOS의 원래 서버 명령을 그대로 복원한 것이 아니다.
의미:
type 12 임시 센서 보고 설정
up_itvl 10 10초 간격 보고 요청(정확한 주기 동작 여부 미확인)
duration 86400 24시간 유지
이 명령은 Home Assistant가 10초마다 장치를 폴링한다는 뜻이 아니다. Home Assistant가 명령을 한 번 보내 장치에 임시 보고 모드를 요청한다. 장치는 지정된 기간 동안 MQTT로 값을 push하지만, ACM-B1MOS에서 실제 발행 간격은 정확히 10초로 고정되지 않고 달라질 수 있다.
장치는 다음 data topic으로 값을 발행한다.
sansa/data/7EF6FAA4016711EBA78F00163E000886
정상 브로커 로그:
Received SUBSCRIBE from 7EF6...
Sending PUBLISH to 7EF6... 'sansa/command/7EF6...'
Received PUBLISH from 7EF6... 'sansa/data/7EF6...'
8. 확인된 MQTT payload 구조
외부 JSON의 sensor_data가 Base64 문자열이며, 디코딩 결과도 JSON이다.
센서 데이터는 한 메시지에 전부 들어오지 않고 크게 두 묶음으로 나뉘어 전송됐다.
환경 센서 묶음
{
"humidity": {
"status": "sampling",
"timestamp": 1785033480,
"unit": "%",
"value": 5639
},
"illuminance": {
"timestamp": 1785033480,
"unit": "lux",
"value": 1
},
"temperature": {
"status": "sampling",
"timestamp": 1785033480,
"unit": "C",
"value": 2554
}
}
공기질 센서 묶음
{
"battery": {
"status": "discharge",
"value": 90
},
"co2": {
"level": "unhealthy",
"status": "sampling",
"unit": "ppm",
"value": 190700
},
"illuminance": {
"unit": "lux",
"value": 1
},
"pm1": {
"level": "good",
"status": "sampling",
"unit": "µg/m³",
"value": 500
},
"pm2": {
"level": "good",
"status": "sampling",
"unit": "µg/m³",
"value": 500
},
"pm10": {
"level": "good",
"status": "sampling",
"unit": "µg/m³",
"value": 500
},
"tvoc": {
"level": "good",
"status": "sampling",
"unit": "ppb",
"value": 9100
}
}
확인된 스케일:
| 필드 | 원시 값 예 | HA 값 | 변환 |
|---|---|---|---|
| temperature | 2554 | 25.54 °C | / 100 |
| humidity | 5639 | 56.39 % | / 100 |
| co2 | 190700 | 1907 ppm | / 100 |
| pm1 | 500 | 5 µg/m³ | / 100 |
| pm2 | 500 | 5 µg/m³ | / 100 |
| pm10 | 500 | 5 µg/m³ | / 100 |
| tvoc | 9100 | 91 ppb | / 100 |
| illuminance | 1 | 1 lx | 그대로 |
| battery | 90 | 90 % | 그대로 |
pm2는 ACM-B1MOS의 PM2.5 값이다. TVOC 9100이면서 level: good이므로 실제 값은 91 ppb로 해석한다.
9. Home Assistant 파일 구성
configuration.yaml:
mqtt: !include mqtt.yaml
mqtt.yaml에는 mqtt:를 다시 쓰지 않고 바로 sensor:부터 작성한다.
동일한 device.identifiers를 사용하면 여러 센서 엔티티가 Home Assistant의 에어모니터 장치 하나 아래에 묶인다.
10. mqtt.yaml
sensor:
- name: "온도"
unique_id: air_monitor_temperature
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: temperature
unit_of_measurement: "°C"
state_class: measurement
suggested_display_precision: 2
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.temperature is defined %}
{{ (data.temperature.value | float / 100) | round(2) }}
{% else %}
{{ '' }}
{% endif %}
device: &air_monitor_device
identifiers:
- samsung_air_monitor_acm_b1mos
name: "에어모니터"
manufacturer: "Samsung"
model: "ACM-B1MOS"
sw_version: "1.1.38"
- name: "습도"
unique_id: air_monitor_humidity
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: humidity
unit_of_measurement: "%"
state_class: measurement
suggested_display_precision: 2
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.humidity is defined %}
{{ (data.humidity.value | float / 100) | round(2) }}
{% else %}
{{ '' }}
{% endif %}
device: *air_monitor_device
- name: "이산화탄소"
unique_id: air_monitor_co2
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: carbon_dioxide
unit_of_measurement: "ppm"
state_class: measurement
suggested_display_precision: 0
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.co2 is defined %}
{{ (data.co2.value | float / 100) | round(0) }}
{% else %}
{{ '' }}
{% endif %}
device: *air_monitor_device
- name: "이산화탄소 상태"
unique_id: air_monitor_co2_level
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
icon: mdi:molecule-co2
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% set level = data.co2.level %}
{% set labels = {
'excellent': '매우 좋음',
'good': '좋음',
'moderate': '보통',
'unhealthy': '나쁨',
'very_unhealthy': '매우 나쁨',
'hazardous': '위험'
} %}
{{ labels.get(level, level) }}
device: *air_monitor_device
- name: "극초미세먼지 PM1"
unique_id: air_monitor_pm1
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: pm1
unit_of_measurement: "µg/m³"
state_class: measurement
suggested_display_precision: 1
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.pm1 is defined %}
{{ (data.pm1.value | float / 100) | round(1) }}
{% else %}
{{ '' }}
{% endif %}
device: *air_monitor_device
- name: "PM1 상태"
unique_id: air_monitor_pm1_level
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
icon: mdi:air-filter
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% set level = data.pm1.level %}
{% set labels = {
'excellent': '매우 좋음',
'good': '좋음',
'moderate': '보통',
'unhealthy': '나쁨',
'very_unhealthy': '매우 나쁨',
'hazardous': '위험'
} %}
{{ labels.get(level, level) }}
device: *air_monitor_device
- name: "초미세먼지 PM2.5"
unique_id: air_monitor_pm25
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: pm25
unit_of_measurement: "µg/m³"
state_class: measurement
suggested_display_precision: 1
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.pm2 is defined %}
{{ (data.pm2.value | float / 100) | round(1) }}
{% else %}
{{ '' }}
{% endif %}
device: *air_monitor_device
- name: "PM2.5 상태"
unique_id: air_monitor_pm25_level
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
icon: mdi:air-filter
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% set level = data.pm2.level %}
{% set labels = {
'excellent': '매우 좋음',
'good': '좋음',
'moderate': '보통',
'unhealthy': '나쁨',
'very_unhealthy': '매우 나쁨',
'hazardous': '위험'
} %}
{{ labels.get(level, level) }}
device: *air_monitor_device
- name: "미세먼지 PM10"
unique_id: air_monitor_pm10
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: pm10
unit_of_measurement: "µg/m³"
state_class: measurement
suggested_display_precision: 1
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.pm10 is defined %}
{{ (data.pm10.value | float / 100) | round(1) }}
{% else %}
{{ '' }}
{% endif %}
device: *air_monitor_device
- name: "PM10 상태"
unique_id: air_monitor_pm10_level
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
icon: mdi:air-filter
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% set level = data.pm10.level %}
{% set labels = {
'excellent': '매우 좋음',
'good': '좋음',
'moderate': '보통',
'unhealthy': '나쁨',
'very_unhealthy': '매우 나쁨',
'hazardous': '위험'
} %}
{{ labels.get(level, level) }}
device: *air_monitor_device
- name: "TVOC"
unique_id: air_monitor_tvoc
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: volatile_organic_compounds_parts
unit_of_measurement: "ppb"
state_class: measurement
suggested_display_precision: 0
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.tvoc is defined %}
{{ (data.tvoc.value | float / 100) | round(0) }}
{% else %}
{{ '' }}
{% endif %}
device: *air_monitor_device
- name: "TVOC 상태"
unique_id: air_monitor_tvoc_level
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
icon: mdi:chemical-weapon
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% set level = data.tvoc.level %}
{% set labels = {
'excellent': '매우 좋음',
'good': '좋음',
'moderate': '보통',
'unhealthy': '나쁨',
'very_unhealthy': '매우 나쁨',
'hazardous': '위험'
} %}
{{ labels.get(level, level) }}
device: *air_monitor_device
- name: "조도"
unique_id: air_monitor_illuminance
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: illuminance
unit_of_measurement: "lx"
state_class: measurement
suggested_display_precision: 0
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.illuminance is defined %}
{{ data.illuminance.value | float | round(0) }}
{% else %}
{{ '' }}
{% endif %}
device: *air_monitor_device
- name: "배터리"
unique_id: air_monitor_battery
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
device_class: battery
unit_of_measurement: "%"
state_class: measurement
suggested_display_precision: 0
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% if data.battery is defined %}
{{ data.battery.value | int }}
{% else %}
{{ '' }}
{% endif %}
device: *air_monitor_device
- name: "배터리 상태"
unique_id: air_monitor_battery_status
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
entity_category: diagnostic
icon: mdi:battery-heart-variant
value_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{% set status = data.battery.status %}
{% set labels = {
'discharge': '방전 중',
'discharging': '방전 중',
'charge': '충전 중',
'charging': '충전 중',
'full': '충전 완료'
} %}
{{ labels.get(status, status) }}
device: *air_monitor_device
- name: "원본 Payload"
unique_id: air_monitor_raw_payload
state_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
entity_category: diagnostic
icon: mdi:code-json
value_template: >-
{{ value_json.timestamp }}
json_attributes_topic: "sansa/data/7EF6FAA4016711EBA78F00163E000886"
json_attributes_template: >-
{% set data = value_json.sensor_data | base64_decode | from_json %}
{{ {
'device_id': value_json.device_id,
'sensor_data': data,
'signature': value_json.signature,
'timestamp': value_json.timestamp,
'version': value_json.version
} | tojson }}
device: *air_monitor_device
level 센서는 해당 센서가 없는 다른 묶음의 메시지를 받을 때 템플릿 평가가 실패할 수 있다. Home Assistant MQTT 센서는 value template 오류 시 기존 상태를 유지하므로 환경 센서 묶음과 공기질 센서 묶음이 번갈아 와도 마지막 정상 상태가 보존된다.
11. Home Assistant UI 자동화
Home Assistant의 자동화 생성 화면에서 YAML 편집 모드로 전환한 뒤 다음을 붙여 넣는다.
description: "에어모니터에 10초 주기 센서 데이터 보고를 요청합니다."
mode: single
triggers:
- trigger: homeassistant
event: start
- trigger: time_pattern
hours: "/12"
conditions: []
actions:
- delay:
seconds: 10
- action: mqtt.publish
data:
topic: "sansa/command/7EF6FAA4016711EBA78F00163E000886"
payload: '{"type":"12","up_itvl":"10","duration":"86400"}'
qos: 0
retain: true
동작 방식:
- Home Assistant 시작 10초 후 명령 발행
- 이후 12시간마다 명령 갱신
- 장치에 10초 보고 간격을 요청함(실제 관찰 간격은 약 20초~1분으로 가변적이며 정확한 동작 여부 미확인)
- 한 번 받은 명령은 24시간 유지
- retained 명령이므로 장치가 재접속하면 즉시 다시 전달
12. 적용과 확인
configuration.yaml과mqtt.yaml저장- Home Assistant 개발자 도구에서 구성 검사
- Home Assistant 재시작
- 설정 → 장치 및 서비스 → MQTT 확인
에어모니터장치 하나 아래 엔티티가 묶였는지 확인- 자동화를 수동 실행하거나 장치를 완전히 재부팅
- MQTT broker 로그에서 data topic 발행 확인
최종 엔티티:
에어모니터
├─ 온도
├─ 습도
├─ 이산화탄소
├─ 이산화탄소 상태
├─ 극초미세먼지 PM1
├─ PM1 상태
├─ 초미세먼지 PM2.5
├─ PM2.5 상태
├─ 미세먼지 PM10
├─ PM10 상태
├─ TVOC
├─ TVOC 상태
├─ 조도
├─ 배터리
├─ 배터리 상태
└─ 원본 Payload
13. 문제 해결
MQTT에 장치 접속이 보이지 않음
- USB만 분리하지 말고 내장 배터리까지 포함해 장치를 완전히 종료한다.
nslookup으로 MQTT 도메인이 내부 IP를 반환하는지 확인한다.- 공유기 tcpdump에서 DNS 응답과 TCP 1883 SYN을 확인한다.
- MQTT 브로커가 LAN의 TCP 1883에서 실제로 수신 중인지 확인한다.
command topic만 보이고 data topic이 없음
- command 메시지가 retained인지 확인한다.
- 브로커 로그에
Sending PUBLISH to 7EF6...가 있는지 확인한다. - 장치를 완전히 재부팅한다.
- command payload의
type,up_itvl,duration을 확인한다.
센서 일부만 한 번에 보임
정상 동작이다. 장치는 온도·습도 묶음과 공기질 묶음을 별도 메시지로 전송한다. mqtt.yaml의 숫자 센서는 해당 키가 없는 메시지에서 빈 값을 반환하여 이전 값을 유지한다.
SmartThings와 동시에 사용할 수 있는가
현재 DNS 우회 대상은 MQTT 도메인뿐이며 sansa.cleargrass.com HTTPS는 원래 서버로 유지한다. 따라서 SmartThings 클라우드 통신이 계속될 가능성이 높지만, 장기 동작과 펌웨어별 차이는 별도로 확인해야 한다.
14. 보안 주의사항
- MQTT TCP 1883은 평문이므로 LAN 패킷을 볼 수 있는 사용자는 인증정보와 센서 값을 읽을 수 있다.
acm-mqtt-auth.pcap에는 MQTT 비밀번호가 포함될 수 있다.- PCAP, 비밀번호 파일, MQTT 비밀번호를 공개 Git 저장소에 커밋하지 않는다.
- 익명 MQTT 브로커는 인터넷과 게스트 네트워크에 노출하지 않는다.
signature값은 현재 로컬 센서 읽기에는 사용하지 않지만 원본 데이터의 일부이므로 불필요하게 공개하지 않는다.