본문 바로가기
Hardware/Home Assistant

Home Assistant, 더샵 아파트 월패드 연동 3편 - ESPHome(RS485) 중계 모듈 설치

by lovey25 2020. 11. 25.

더샵 아파트 월패드(홈네트워크) 연동의 마무리를 지어보도록 하겠습니다.

 

HomeAssistant, 더샵 아파트 월패드 연동 2편 - EW11로 RS485 패킷 분석

더샵 아파트 월패드를 Home Assistant(HA)에 연결하기 두 번째 포스팅입니다. 아파트 홈네트워크가 어떻게 구성되어 있는지 지난 포스팅에서 아주 가볍게 알아봤었죠. 이제 방법을 알아버렸기 때문

kwonkyo.tistory.com

지난 포스팅까지 연동할 RS485 통신 패킷까지 확인을 했었는데요. 이제 이 패킷을 네이버 Home Assistant 카페에 까꿍tm님께서 공유해주신 방법에 적용하기만 하면 됩니다. 이 방법은 ESP8266 보드에 RS485-TTL 변환 모듈을 연결하고 ESPHome을 이용해서 ESP8266 보드를 RS485 통신 요소로써 Home Assistant(HA)의 통합 구성요소로 바로 이식할 수 있도록 해 줍니다.

하드웨어

컨트롤러는 UART 포트 사용 가능한 어떤 보드라도 가능하지만 사용하기 편리하고 나중에 에어컨이나 현관 자석 센서 등 확장도 생각해서 wemos D1 mini를 사용했습니다. 그 외 RS485-TTL 변환 모듈과 UTP 케이블을 준비했습니다. 그리고 핸드폰 충전기가 있으면 USB 단자로 전원을 공급할 수도 있지만 통신반 내부에 콘센트 여분이 없어서 IO 모듈의 12V 전원을 바로 이용하기 위해서 AMS1117-5V 모듈도 준비했습니다. 

그리고 최대한 부피를 적게 차지하도록 방판에 올려 납땜하였습니다. 결선 방법은 위 그림으로 대신하겠습니다. 

AMS1117 모듈은 Datasheet에 따르면 입력 전원으로 12V도 충분히 사용 가능한 것으로 보이나 연결했을 때 발열이 심해서(50˚C 정도?) 방열핀을 달아주었습니다. 효과가 얼마나 있을지는 모르겠지만 며칠 사용해 본 결과 문제없이 잘 돌아가고 있습니다. 

소프트웨어

이제 RS485-WiFi 변환 모듈의 하드웨어가 완성되었으니 펌웨어 올려보겠습니다. 먼저 HA에 ESPHome Add-on이 설치되어야 하는데 Add-on store에서 기본적으로 찾을 수 있는 ESPHome이 아닌 다른 버전을 설치해야 합니다. 기본 ESPHome에는 RS485 컴포넌트가 포함되어 있지 않기 때문인데요.

Add-on store에서 우측 상단의 메뉴에서 "Repositories"를 선택해서 새로운 저장소 주소를 추가합니다. (https://github.com/greays/hassio)

이제 아래와 같이 4가지 추가 Add-ons을 확인할 수 있는데요. 이 중에서 "ESPHome (rs485)"를 설치합니다. 기존에 ESPHome이 설치되어 있더라도 그냥 추가로 설치하면 됩니다. ESPHome 2가지 버전이 따로따로 설치되는 게 아니라 같은 폴더에 덮어쓰는 형식으로 진행되는 것 같습니다. 찜찜하면 기존의 ESPHome을 지우소 다시 설치하면 되는데 전 그냥 추가로 설치하고 기존 Add-on은 비활성화만 해 두었는 데 사용하는데 아무 지장은 없습니다.

ESPHome은 현재 1.15.3 버전까지 공개가 되었는데 rs485 포함 버전은 아직 1.15.2 버전입니다. 자세한 내용은 잘 모르지만 소스 구성을 보니 다음과 같이 6가지의 컴포넌트 형태를 지원하는 것 같습니다.

"greays"라는 분이 ESPHome 저장소를 포킹 해서 RS485 기능을 추가한 다음 재배포하는 것 같은데 앞으로도 지속적인 업데이트가 있는 것인지 그런 정보는 없는 것 같습니다. 계속 유지보수가 이어지면 좋겠네요.

아무튼 설치가 끝났으면 RS485를 위한 노드를 추가합니다. 그리고 아래의 yaml 스크립트를 이용해서 펌웨어를 만들어 줍니다.

더보기
esphome:
  name: thesharp
  platform: ESP8266
  board: esp12e
 
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
 
  ap:
    ssid: "SOS_theSharp"
    password: !secret wifi_password
 
captive_portal:
 
# Enable logging
logger:
  baud_rate: 0 #9600
  level: INFO
  #level: DEBUG #기본값
  #level: VERBOSE #사용하지 않는 패킷만 출력
  #level: VERY_VERBOSE #전체 패킷 출력 (초기 설정시에 추천)
 
# Enable Home Assistant API
api:
  password: !secret ota_password

ota:
  password: !secret ota_password
 
status_led:
  pin:
    number: GPIO2
    inverted: True

# RS485 Component (for ttl to rs485 module)
#  - esp8266: UART0 (TX: GPIO1, RX: GPIO3)
#  - esp32: UART2 (TX: GPIO17, RX: GPIO16)
rs485:
  baud_rate: 9600 #Required
  data_bits: 8    #Option(default: 8)
  parity: 0       #Option(default: 0)
  stop_bits: 1    #Option(default: 1)
 
  rx_wait: 10     #Option(default: 10ms)  -> 수신 메시지 대기시간 (10ms 미만으로 수신된 메시지만 한 패킷으로 판단)
  tx_interval: 50 #Option(default: 50ms) -> 발신 메시지 전송 간격 (패킷 수신 후 50ms 대기 후 전송)
  tx_wait: 100    #Option(default: 100ms) -> 발신 메시지 Ack 대기시간
  tx_retry_cnt: 3 #Option(default: 3)     -> 발신 메시지 Ack 없을 경우 재시도 횟수
  prefix: [0xF7]  #Option -> 값 세팅시 모든 수신 패킷 Check, 발신 패킷에 Append
  suffix: [0xEE]  #Option -> 값 세팅시 모든 수신 패킷 Check, 발신 패킷에 Append

  checksum: True      #Option(default: False) -> 체크섬 사용여부 (lambda 사용시 세팅 불필요)
  # checksum_lambda: |- #Option -> Default(CheckSum8 Xor) 체크섬 아닐 경우 직접 로직 구현(아래 값은 Default 로직임)
  #   // @param: const uint8_t *data, const unsigned short len
  #   // @return: uint8_t
  #   uint8_t crc = 0xF7; // data 변수에는 prefix 제외되어 있음
  #   for(num_t i=0; i<len; i++)
  #     crc ^= data[i];
  #   return crc;
 
  state_response: #Option -> 값 세팅시 response 패킷 수신 후에 명령 패킷 송신
    data: [0x04]
    offset: 3
 
  packet_monitor: #Option -> 패킷 모니터: Array 없으면 전체 출력, 있을 경우 or 조건 (logger level DEBUG 추천)
    - [0x0D, 0x01, 0x1B] #0ffset:0
    #- data: [0x04]
    #  offset: 3

# RS485 Device 셋팅 팁
  # 1. 상태 확인용(수신) Schema
  #   - 대상: device(Required), sub_device(Option), state*
  #   - 조건: device and sub_device 일치할때만 해당 Device에 패킷 전달됨
  #   - 예시:
  #     device:
  #       offset: 0 # 수신 패킷 비교 위치 (prefix 제외)
  #       data: [0x0b, 0x01, 0x19, 0x04, 0x40, 0x23, 0x00] # hex array
  #     #alias schema
  #     device: [0x0b, 0x01, 0x19, 0x04, 0x40, 0x23, 0x00] # offset: 0, data: hex array
  #
  # 2. 명령용(발신) Schema
  #   - 대상: command*
  #   - 예시:
  #     command:
  #       data: [0x0b, 0x01, 0x1f, 0x02, 0x40, 0x21, 0x01, 0x00]  # prefix, checksum, suffix 제외
  #       ack: [0x0b, 0x01, 0x1f, 0x04, 0x40, 0x21, 0x01, 0x01]   # prefix, checksum, suffix 제외
  #     # Ack 없을 경우
  #     command: [0x0b, 0x01, 0x1f, 0x02, 0x40, 0x21, 0x01, 0x00]
  #     # Template(lambda): command_on, command_off
  #     command_on: !lambda |-
  #       uint8_t flag = id(balcony).state ? 0x02 : 0x01;
  #       return {
  #                 {0x0b, 0x01, 0x19, 0x02, 0x40, 0x23, flag, 0x00},
  #                 {0x0b, 0x01, 0x19, 0x04, 0x40, 0x23, 0x01, 0x01}
  #              };
  #     command_off: !lambda |-
  #       uint8_t flag = id(balcony).state ? 0x02 : 0x01;
  #       return {
  #                 {0x0b, 0x01, 0x19, 0x02, 0x40, 0x23, flag, 0x00},
  #                 {0x0b, 0x01, 0x19, 0x04, 0x40, 0x23, 0x02, 0x02}
  #              };
  #     # 상태확인 요청 (Sensor 포함 모든 Device에 사용 가능)
  #     command_state: [0x0B, 0x01, 0x19, 0x01, 0x40, 0x23, 0x00, 0x00] #Option
  #     update_interval: 30s #Option(default: 60s)
  #
  # 3. Automation Schema
  #   - rs485.write:
  #       data: [0x0b, 0x01, 0x19, 0x02, 0x40, 0x23, 0x01, 0x00]
  #       ack: [0x0b, 0x01, 0x19, 0x04, 0x40, 0x23, 0x01, 0x01]
  #   - rs485.write: !lambda return {{0x0b, 0x01, 0x19, 0x02, 0x40, 0x23, 0x01, 0x00}, {0x0b, 0x01, 0x19, 0x04, 0x40, 0x23, 0x01, 0x01}};
  #   # Ack 없는 경우
  #   - rs485.write: [0x0b, 0x01, 0x19, 0x02, 0x40, 0x23, 0x01, 0x00]
  #   - rs485.write: !lambda return {{0x0b, 0x01, 0x19, 0x02, 0x40, 0x23, 0x01, 0x00}, {}};

# RS485 Light(like Binary Light) ###################################
#현대통신
#거실- 3구 (3Way 포함)
  #상태조회
  #>F7 0B 01 19 01 40 x1 00 00 << (x1 - 11:거실1, 12:거실2, 13:3Way)
  #<F7 0B 01 19 04 40 x1 00 x2 << (x2 - 02:off, 01:on#)
  #명령  
  #>F7 0B 01 19 02 40 x1 x3 00 << (x3 - 01:on, 02:off)
  #<F7 0B 01 19 04 40 x1 x4 x4 << (x4 - 0101:on, 0202:off)
light:
  - platform: rs485
    name: "Livingroom1"
    device: [0x0b, 0x01, 0x19, 0x04, 0x40, 0x11, 0x00]
    state_on:
      offset: 7
      data: [0x01]
    state_off:
      offset: 7
      data: [0x02]
    command_on:
      data: [0x0b, 0x01, 0x19, 0x02, 0x40, 0x11, 0x01, 0x00]
      ack:  [0x0b, 0x01, 0x19, 0x04, 0x40, 0x11, 0x01, 0x01]
    command_off:
      data: [0x0b, 0x01, 0x19, 0x02, 0x40, 0x11, 0x02, 0x00]
      ack:  [0x0b, 0x01, 0x19, 0x04, 0x40, 0x11, 0x02, 0x02]
    command_state: [0x0b, 0x01, 0x19, 0x01, 0x40, 0x11, 0x00, 0x00] # 요청은 한곳에서만
    update_interval: 20s # 상태요청 주기

  - platform: rs485
    name: "Livingroom2"
    device: [0x0b, 0x01, 0x19, 0x04, 0x40, 0x12, 0x00]
    state_on:
      offset: 7
      data: [0x01]
    state_off:
      offset: 7
      data: [0x02]
    command_on:
      data: [0x0b, 0x01, 0x19, 0x02, 0x40, 0x12, 0x01, 0x00]
      ack:  [0x0b, 0x01, 0x19, 0x04, 0x40, 0x12, 0x01, 0x01]
    command_off:
      data: [0x0b, 0x01, 0x19, 0x02, 0x40, 0x12, 0x02, 0x00]
      ack:  [0x0b, 0x01, 0x19, 0x04, 0x40, 0x12, 0x02, 0x02]
    command_state: [0x0b, 0x01, 0x19, 0x01, 0x40, 0x12, 0x00, 0x00] # 요청은 한곳에서만
    update_interval: 21s # 상태요청 주기

  - platform: rs485
    name: "3Way"
    device: [0x0b, 0x01, 0x19, 0x04, 0x40, 0x13, 0x00]
    state_on:
      offset: 7
      data: [0x01]
    state_off:
      offset: 7
      data: [0x02]
    command_on:
      data: [0x0b, 0x01, 0x19, 0x02, 0x40, 0x13, 0x01, 0x00]
      ack:  [0x0b, 0x01, 0x19, 0x04, 0x40, 0x13, 0x01, 0x01]
    command_off:
      data: [0x0b, 0x01, 0x19, 0x02, 0x40, 0x13, 0x02, 0x00]
      ack:  [0x0b, 0x01, 0x19, 0x04, 0x40, 0x13, 0x02, 0x02]
    command_state: [0x0b, 0x01, 0x19, 0x01, 0x40, 0x13, 0x00, 0x00] # 요청은 한곳에서만
    update_interval: 22s # 상태요청 주기
# RS485 Light(like Binary Light) ###################################


###################################
# RS485 Fan
fan:
  #F70B 012B 0140 1100 0086 EE  >>성태조회 MainTouch
  #F70C 012B 0440 1100 0207 81EE  <<응답상태끔
  #F70C 012B 0440 1100 0101 84EE  <<응답상태약
  #F70C 012B 0440 1100 0103 86EE  <<응답상태중
  #F70C 012B 0440 1100 0107 82EE  <<응답상태강

  #환기 off cmd
  #>f70b 012b 0240 1102 0087ee
  #<f70c 012b 0441 2b04 4011 00020781ee
  #환기 on cmd
  #>f70b 012b 0242 1101 0086ee
  #<f70c 012b 0441 2b04 4011 00010184ee

  #환기 on 3단 cmd
  #>f70b 012b 0242 1107 0080ee
  #<f70c 012b 0441 2b04 4011 0001 0782ee
  #환기 on 2단 cmd
  #>f70b 012b 0242 1103 0084ee
  #<f70c 012b 0441 2b04 4011 0001 0386ee
  #환기 on 1단 cmd
  #>f70b 012b 0242 1101 0086ee
  #<f70c 012b 0441 2b04 4011 0001 0184ee
  - platform: rs485
    name: "Ventilation"
    device: [0x0c, 0x01, 0x2b, 0x04, 0x40, 0x11, 0x00]
    state_on:
      offset: 7
      data: [0x01]
    state_off:
      offset: 7
      data: [0x02]
    command_on:
      data: [0x0b, 0x01, 0x2b, 0x02, 0x42, 0x11, 0x01, 0x00]
      ack:  [0x0c, 0x01, 0x2b, 0x04, 0x41, 0x2b, 0x04, 0x40, 0x11, 0x00, 0x01]
    command_off:
      data: [0x0b, 0x01, 0x2b, 0x02, 0x40, 0x11, 0x02, 0x00]
      ack: [0x0c, 0x01, 0x2b, 0x04, 0x40, 0x2b, 0x04, 0x40, 0x11, 0x00, 0x02]
    speed: #Option(high, medium, low) -> 없으면 Binary Fan
      high:
        state:
          offset: 7
          data: [0x01, 0x07]
        command:
          data: [0x0b, 0x01, 0x2b, 0x02, 0x42, 0x11, 0x07, 0x00]
          ack:  [0x0c, 0x01, 0x2b, 0x04, 0x41, 0x2b, 0x04, 0x11, 0x00, 0x01, 0x07]
      medium:
        state:
          offset: 7
          data: [0x01, 0x03]
        command:
          data: [0x0b, 0x01, 0x2b, 0x02, 0x42, 0x11, 0x03, 0x00]
          ack: [0x0c, 0x01, 0x2b, 0x04, 0x41, 0x2b, 0x04, 0x11, 0x00, 0x01, 0x03]
      low:
        state:
          offset: 7
          data: [0x01, 0x01]
        command:
          data: [0x0b, 0x01, 0x2b, 0x02, 0x42, 0x11, 0x01, 0x00]
          ack: [0x0c, 0x01, 0x2b, 0x04, 0x41, 0x2b, 0x04, 0x11, 0x00, 0x01, 0x01]
    command_state: [0x0B, 0x01, 0x2B, 0x01, 0x40, 0x11, 0x00, 0x00] # 요청은 한곳에서만
    update_interval: 50s # 상태요청 주기         
###################################

switch:
  # 콘센트는 해당사항 없어서 제외
  # - platform: rs485
  #   name: "Livingroom Power Socket 1"
  #   icon: "mdi:power-socket-eu"
  #   device: [0x1C, 0x01, 0x1F, 0x04, 0x40, 0x10, 0x00]
  #   state_on:
  #     offset: 8
  #     data: [0x01]
  #   state_off:
  #     offset: 8
  #     data: [0x02]
  #   command_on:
  #     data: [0x0b, 0x01, 0x1f, 0x02, 0x40, 0x11, 0x01, 0x00]
  #     ack:  [0x0b, 0x01, 0x1f, 0x04, 0x40, 0x11, 0x01, 0x01]
  #   command_off:
  #     data: [0x0b, 0x01, 0x1f, 0x02, 0x40, 0x11, 0x02, 0x00]
  #     ack:  [0x0b, 0x01, 0x1f, 0x04, 0x40, 0x11, 0x02, 0x02]
  #   command_state: [0x0B, 0x01, 0x1F, 0x01, 0x40, 0x10, 0x00, 0x00] # 요청은 한곳에서만
  #   update_interval: 30s # 상태요청 주기 
      
  #도어락#################################################
  #F7 0E 01 1E 02 43 11 04 00 04 FF FF B6 EE
  #F7 0C 01 1E 04 43 11 04 00 04 B2 EE
  #switch:
  - platform: template
    name: doorlock
    turn_on_action:
      - rs485.write:
          data: [0x0e, 0x01, 0x1e, 0x02, 0x43, 0x11, 0x04, 0x00, 0x04, 0xff, 0xff]
          ack: [0x0c, 0x01, 0x1e, 0x04, 0x43, 0x11, 0x04, 0x00, 0x04]
      
# RS485 Climate
climate:
  # [거실 난방] 0x11
  # 상태 요청: 0xF7, 0x0B, 0x01, 0x18, 0x01, 0x45, 0x11, 0x00, 0x00, 0xB0, 0xEE
  # 켜짐 상태: 0xF7, 0x0D, 0x01, 0x18, 0x04, 0x45, 0x11, 0x00, (0x01, 0x1B, 0x17), 0xBE, 0xEE (상태, 현재온도, 설정온도)
  # 꺼짐 상태: 0xF7, 0x0D, 0x01, 0x18, 0x04, 0x45, 0x11, 0x00, (0x04, 0x1B, 0x17), 0xBB, 0xEE (상태, 현재온도, 설정온도)
  # 외출 상태: 0xF7, 0x0D, 0x01, 0x18, 0x04, 0x45, 0x11, 0x00, (0x07, 0x1B, 0x17), 0xB9, 0xEE
  # 켜짐 명령: 0xF7, 0x0B, 0x01, 0x18, 0x02, 0x46, 0x11, 0x01, 0x00, 0xB1, 0xEE
  #      ACK: 0xF7, 0x0D, 0x01, 0x18, 0x04, 0x46, 0x11, 0x01, 0x01, 0x1B, 0x17, 0xBC, 0xEE
  # 꺼짐 명령: 0xF7, 0x0B, 0x01, 0x18, 0x02, 0x46, 0x11, 0x04, 0x00, 0xB4, 0xEE
  #      ACK: 0xF7, 0x0D, 0x01, 0x18, 0x04, 0x46, 0x11, 0x04, 0x04, 0x1B, 0x17, 0xBC, 0xEE
  # 온도 조절: 0xF7, 0x0B, 0x01, 0x18, 0x02, 0x45, 0x11, (0x18), 0x00, 0xA7, 0xEE (온도 24도 설정)
  #      ACK: 0xF7, 0x0D, 0x01, 0x18, 0x04, 0x45, 0x11, (0x18), 0x01, (0x1A, 0x18), 0xA8, 0xEE
  - platform: rs485
    name: "Livingroom Heater"
    visual:
      min_temperature: 5 °C
      max_temperature: 40 °C
      temperature_step: 1 °C
    device: [0x0D, 0x01, 0x18, 0x04, 0x46, 0x11, 0x00]
    state_heat: #Option (난방모드, 냉방모드: state_cool, 자동모드: state_auto)
      offset: 7
      data: [0x01]
    state_off: #Required (끄기 상태)
      offset: 7
      data: [0x04]
    state_away: #Option (외출모드)
      offset: 7
      data: [0x07]
    state_current: #Required (현재온도 State, RS485 Sensor 설정 참고, sensor:로 대체 가능)
      offset: 8
      length: 1
      precision: 0
    state_target: #Required (설정온도 State)
      offset: 9
      length: 1
      precision: 0

    command_off: #Required (끄기 명령)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x11, 0x04, 0x00]
      ack: [0x0D, 0x01, 0x18, 0x04, 0x46, 0x11, 0x04, 0x04]
    command_heat: #Option (난방모드 켜기)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x11, 0x01, 0x00]
      ack: [0x0D, 0x01, 0x18, 0x04, 0x46, 0x11, 0x01, 0x01]
    command_away: #Option (외출 켜기)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x11, 0x07, 0x00]
      ack: [0x0D, 0x01, 0x18, 0x04, 0x46, 0x11, 0x07, 0x07]
    command_temperature: !lambda |-  #Required (온도 조절)
      // @param: const float x
      return {
               {0x0B, 0x01, 0x18, 0x02, 0x45, 0x11, (uint8_t)x, 0x00},
               {0x0D, 0x01, 0x18, 0x04, 0x45, 0x11, (uint8_t)x, 0x01}
            };
    command_state: [0x0B, 0x01, 0x18, 0x01, 0x46, 0x11, 0x00, 0x00] # 요청은 한곳에서만
    update_interval: 60s # 상태요청 주기

  - platform: rs485
    name: "room1 Heater"
    visual:
      min_temperature: 5 °C
      max_temperature: 40 °C
      temperature_step: 1 °C
    device: [0x0D, 0x01, 0x18, 0x04, 0x46, 0x12, 0x00]
    state_current: #Required (현재온도 State, RS485 Sensor 설정 참고, sensor:로 대체 가능)
      offset: 8
      length: 1
      precision: 0
    state_target: #Required (설정온도 State)
      offset: 9
      length: 1
      precision: 0
    state_off: #Required (끄기 상태)
      offset: 7
      data: [0x04]
    state_heat: #Option (난방모드, 냉방모드: state_cool, 자동모드: state_auto)
      offset: 7
      data: [0x01]
    state_away: #Option (외출모드)
      offset: 7
      data: [0x07]
    command_off: #Required (끄기 명령)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x12, 0x04, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x12, 0x04, 0x04]
    command_heat: #Option (난방모드 켜기)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x12, 0x01, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x12, 0x01, 0x01]
    command_away: #Option (외출 켜기)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x12, 0x07, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x12, 0x07, 0x07]
    command_temperature: !lambda |-  #Required (온도 조절)
      // @param: const float x
      return {
               {0x0B, 0x01, 0x18, 0x02, 0x45, 0x12, (uint8_t)x, 0x00},
                {0x0D, 0x01, 0x18, 0x04, 0x45, 0x12, (uint8_t)x, 0x01}
            };
    command_state: [0x0B, 0x01, 0x18, 0x01, 0x46, 0x12, 0x00, 0x00] # 요청은 한곳에서만
    update_interval: 61s # 상태요청 주기

  - platform: rs485
    name: "room2 Heater"
    visual:
      min_temperature: 5 °C
      max_temperature: 40 °C
      temperature_step: 1 °C
    device: [0x0D, 0x01, 0x18, 0x04, 0x46, 0x13, 0x00]
    state_current: #Required (현재온도 State, RS485 Sensor 설정 참고, sensor:로 대체 가능)
      offset: 8
      length: 1
      precision: 0
    state_target: #Required (설정온도 State)
      offset: 9
      length: 1
      precision: 0
    state_off: #Required (끄기 상태)
      offset: 7
      data: [0x04]
    state_heat: #Option (난방모드, 냉방모드: state_cool, 자동모드: state_auto)
      offset: 7
      data: [0x01]
    state_away: #Option (외출모드)
      offset: 7
      data: [0x07]
    command_off: #Required (끄기 명령)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x13, 0x04, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x13, 0x04, 0x04]
    command_heat: #Option (난방모드 켜기)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x13, 0x01, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x13, 0x01, 0x01]
    command_away: #Option (외출 켜기)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x13, 0x07, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x13, 0x07, 0x07]
    command_temperature: !lambda |-  #Required (온도 조절)
      // @param: const float x
      return {
               {0x0B, 0x01, 0x18, 0x02, 0x45, 0x13, (uint8_t)x, 0x00},
               {0x0D, 0x01, 0x18, 0x04, 0x45, 0x13, (uint8_t)x, 0x01}
            };
    command_state: [0x0B, 0x01, 0x18, 0x01, 0x46, 0x13, 0x00, 0x00] # 요청은 한곳에서만
    update_interval: 62s # 상태요청 주기

  - platform: rs485
    name: "room3 Heater"
    visual:
      min_temperature: 5 °C
      max_temperature: 40 °C
      temperature_step: 1 °C
    device: [0x0D, 0x01, 0x18, 0x04, 0x46, 0x14, 0x00]
    state_current: #Required (현재온도 State, RS485 Sensor 설정 참고, sensor:로 대체 가능)
      offset: 8
      length: 1
      precision: 0
    state_target: #Required (설정온도 State)
      offset: 9
      length: 1
      precision: 0
    state_off: #Required (끄기 상태)
      offset: 7
      data: [0x04]
    state_heat: #Option (난방모드, 냉방모드: state_cool, 자동모드: state_auto)
      offset: 7
      data: [0x01]
    state_away: #Option (외출모드)
      offset: 7
      data: [0x07]
    command_off: #Required (끄기 명령)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x14, 0x04, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x14, 0x04, 0x04]
    command_heat: #Option (난방모드 켜기)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x14, 0x01, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x14, 0x01, 0x01]
    command_away: #Option (외출 켜기)
      data: [0x0B, 0x01, 0x18, 0x02, 0x46, 0x14, 0x07, 0x00]
      ack:  [0x0D, 0x01, 0x18, 0x04, 0x46, 0x14, 0x07, 0x07]
    command_temperature: !lambda |-  #Required (온도 조절)
      // @param: const float x
      return {
               {0x0B, 0x01, 0x18, 0x02, 0x45, 0x14, (uint8_t)x, 0x00},
               {0x0D, 0x01, 0x18, 0x04, 0x45, 0x14, (uint8_t)x, 0x01}
             };
    command_state: [0x0B, 0x01, 0x18, 0x01, 0x46, 0x14, 0x00, 0x00] # 요청은 한곳에서만
    update_interval: 63s # 상태요청 주기
    

원작자께서 스크립트에 주석을 잘 달아놓으셔서 쉽지는 않지만 찬찬히 보다 보면 내용 및 사용법 충분히 이해할 수 있었습니다.

참고로 아래 파일은 원작자이신 까꿍tm님께서 카페에 공개하신 스크립트입니다.

thesharp.yaml
0.03MB

컴파일이 완료되었으면 esptool을 이용해서 펌웨어를 보드에 업로드합니다.

설치

이제 모든 준비가 끝났습니다. 완성된 RS485-WiFi 모듈을 통신반에 설치합니다.

적당히 빈자리에 고정을 하고 4개 핀을 다음과 같이 각각 연결합니다. 

A+ = 흰파, B- = 파랑, VCC = 12V, GND = GND

HA 설정

정상적으로 모든 과정을 마쳤다면 HA에서는 자동으로 새로운 노드를 인식해서 알림을 띄워줍니다.

설정하기를 눌러보면 다음과 같이 9개의 새로운 구성요소가 추가된 것을 알 수 있습니다.

이제 센서 타입별로 모아서 lovelace에 이쁘게 추가해 봅니다.

 

끝!

728x90

댓글3

  • 해하 2021.01.08 18:32

    위에 작성한 댓글의 비밀번호를 모르고 잊었습니다 ㅠㅠ 혹시 댓글 확인하시면 카카오톡 계정은 삭제 부탁드립니다!
    답글

    • Favicon of https://kwonkyo.tistory.com BlogIcon lovey25 2021.01.08 20:02 신고

      (저도 다른 사람이 쓴 댓글 수정은 안되서 일단 휴지통에 넣어놨습니다.)
      “해하”님의 상황은 잘 이해되지만 판매는 어려울것 같습니다. 애초에 본 포스팅의 솔루션이제 고유 창작물도 아니고 그리고 대작이라 하더라도 적정한 이윤을 어떻게 책정해야 할지도 모르겠고 이것 저것 걱정되는게 많아서 조심스럽습니다. 대신 소소한 질의사항이라면 제가 아는 범위안에서는 답변 드리겠습니다. 양해 부탁드립니다. ^^;

  • 서희아빠 2021.02.24 22:54

    딱 제가 하고싶었던 프로젝트르 그대로 하셨네요.

    전 집에 RS485 포트를 못찾아서 데이터+ - 에 연결 해보려고 합니다.

    답글