0254 | ต่อ Sofar Inverter HYD 6000-EP เข้า Home Assistant

วิธีนี้สำหรับ Sofar Inverter ที่ตัว logger มี serial ขึ้นต้นด้วย 23xxxxxxxx นะครับ

[แก้ไข]

สิ่งที่ต้องมีก่อน

  • home assistant
  • mqtt broker (ใช้ public หรือ private ก็ได้ตามสะดวก)
  • linux x86 ซักเครื่องที่เชื่อมต่อไปหา inverter wifi interface ได้ (ใช้บน raspberry pi/arm ไม่ได้เนื่องจากคนเขียนเค้าบอกว่าเขียนรีบๆ มาใช้เอง)
  • โปรแกรม fetch data => load ได้จาก XtheOne/Inverter-Data-Logger issue #37 ดู link sofar-export.zip
  • docker compose

ขั้นตอน

  • แตกไฟล์ sofar-export.zip แล้วเข้าไปใน folder ที่เก็บไฟล์
  • แก้ไฟล์ Dockerfile เป็นตามนี้
FROM golang:1.19-bullseye

ADD . /src

RUN apt update && apt install -y ca-certificates tzdata && \
  cd /src && go build && cp -av /src/sofar /

CMD ["/sofar"]
  • สร้างไฟล์ docker-compose.yml ตามด้านล่าง ใน folder เดียวกัน แก้ค่าสามจุดตามนี้
    • 23xxxxxxxx เป็นเลข serial ที่โชว์ในหน้าเว็บที่เข้าไปดู inverter (login user admin/admin)
    • แก้ค่า tcp://user:pass@mqttserverip:mqttport ตาม mqtt broker ที่ใช้
    • แก้ sofarinverterip เป็นเลข IP ของตัว inverter
services:
  sofar-export:
    image: sofar-export
    build: ./
    restart: unless-stopped
    command: /sofar -logger-serial 23xxxxxxxx -mqtt-url tcp://user:pass@mqttserverip:mqttport -port sofarinverterip:8899
  • เสร็จแล้วสั่ง start docker service ขึ้นมา
docker compose up -d
  • แล้วไปเซ็ต mqtt sensor ใน home assistant (ใส่ใน configuration.yaml) ประมาณนี้ *** parameter ยังไม่ได้ทดสอบ แก้ไขเองตามสะดวก ***
mqtt:
  sensor:
    - name: inverter_Frequency_Grid
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.Frequency_Grid / 100 }}"
      unit_of_measurement: "Hz"
      state_class: "measurement"
      device_class: "frequency"
    - name: inverter_PV_Generation_Today
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.PV_Generation_Today * 10}}"
      unit_of_measurement: "Wh"
      state_class: "measurement"
      device_class: "energy"
    - name: inverter_PV_Generation_Total
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.PV_Generation_Total * 100}}"
      unit_of_measurement: "Wh"
      state_class: "measurement"
      device_class: "energy"
    - name: inverter_ActivePower_Load_Sys
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.ActivePower_Load_Sys * 10}}"
      unit_of_measurement: "W"
      state_class: "measurement"
      device_class: "power"
    - name: inverter_ActivePower_PV_Ext
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.ActivePower_PV_Ext * 10}}"
      unit_of_measurement: "W"
      state_class: "measurement"
      device_class: "power"
    - name: inverter_ActivePower_Output_R
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.ActivePower_Output_R * 10}}"
      unit_of_measurement: "W"
      state_class: "measurement"
      device_class: "power"
    - name: inverter_ActivePower_Output_Total
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.ActivePower_Output_Total * 10}}"
      unit_of_measurement: "W"
      state_class: "measurement"
      device_class: "power"
    - name: inverter_ActivePower_PCC_R
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.ActivePower_PCC_R * 10}}"
      unit_of_measurement: "W"
      state_class: "measurement"
      device_class: "power"
    - name: inverter_ActivePower_PCC_Total
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.ActivePower_PCC_Total * 10}}"
      unit_of_measurement: "W"
      state_class: "measurement"
      device_class: "power"
    - name: inverter_Voltage_Phase_R
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.Voltage_Phase_R / 10}}"
      unit_of_measurement: "V"
      state_class: "measurement"
      device_class: "voltage"
    - name: inverter_Voltage_PV1
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.Voltage_PV1 / 10}}"
      unit_of_measurement: "V"
      state_class: "measurement"
      device_class: "voltage"
    - name: inverter_Voltage_PV2
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.Voltage_PV2 / 10}}"
      unit_of_measurement: "V"
      state_class: "measurement"
      device_class: "voltage"
    - name: inverter_Current_PV1
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.Current_PV1 / 100 }}"
      unit_of_measurement: "A"
      state_class: "measurement"
      device_class: "current"
    - name: inverter_Current_PV2
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.Current_PV2 / 100}}"
      unit_of_measurement: "A"
      state_class: "measurement"
      device_class: "current"
    - name: inverter_Current_Output_R
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.Current_Output_R / 100 }}"
      unit_of_measurement: "A"
      state_class: "measurement"
      device_class: "current"
    - name: inverter_Temperature_Env1
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.Temperature_Env1 }}"
      unit_of_measurement: "C"
      state_class: "measurement"
      device_class: "temperature"
    - name: inverter_Temperature_HeatSink1
      state_topic: "/sensors/energy/inverter2/All"
      value_template: "{{ value_json.HeatSink1 }}"
      unit_of_measurement: "C"
      state_class: "measurement"
      device_class: "temperature"

A001 | เริ่มต้นกับ Ansible – Inventory

Ansible เป็นเครื่องมือจัดการการตั้งค่า Server ที่เราสามารถเขียนบอกว่าต้องการให้มี/ไม่มีอะไรบน server โดยใช้ syntax ของ YAML ในการเขียน โดยโครงสร้างหลักๆ จะแบ่งเป็น 4 ส่วนดังนี้

  1. inventory
  2. playbook
  3. role
  4. module/collection

ซึ่งข้อ 4 จะเป็นสิ่งที่มีสำเร็จรูปมาให้แล้ว แต่อาจต้องโหลดเพิ่มผ่านคำสั่ง ansible-galaxy collection อีกที โดยสามารถดู collection ที่มีอยู่แล้วได้ด้วยคำสั่ง ansible-galaxy collection list ส่วนการทำอะไรต้องการ collection ไหนให้ดูในเอกสารของ ansible module นั้นๆ อีกครั้ง

Inventory

การเริ่มใช้งาน ansible (ไม่นับขั้นตอนติดตั้ง) สิ่งแรกที่เราต้องมีก็คือ inventory file ที่จะให้ ansible รู้ว่าเรากำลังจะจัดการเครื่องไหนบ้าง โดยโครงสร้างที่ง่ายที่สุดคือโครงสร้างแบบ ini file ดังนี้

[groupname]
host1
host2

(สมมติว่าเซฟในชื่อไฟล์ชื่อ hosts)

เมื่อมีไฟล์ inventory แล้ว เราสามารถเรียกใช้ ansible โดยให้เรียกไฟล์ inventory ได้โดยการใส่ parameter -i hosts (แก้คำว่า hosts เป็นชื่อไฟล์ inventory ที่เราสร้างขึ้นมา) เพิ่มในคำสั่ง ansible หรือ ansible-playbook ตอนเราสั่ง หรือสามารถตั้งค่าในไฟล์ ansible.cfg ให้ใช้งานไฟล์ inventory นี้โดยอัตโนมัติก็ได้ โดยสร้างไฟล์ชื่อ ansible.cfg ไว้ใน folder เดียวกัน แล้วใส่ข้อความลักษณะประมาณนี้

[defaults]
inventory = hosts

โดยปกติแล้ว ansible จะเชื่อมต่อไปหา server ตามที่เราระบุผ่าน ssh config และจะพยายามใช้ ssh key ในการเชื่อมต่อ (ถ้ามี) โดยถ้าไฟล์ private key ของเราไม่ใช่ชื่อ ~/.ssh/id_rsa ก็สามารถระบุตัวแปรเพิ่มไปใน inventory ได้ว่าให้แต่ละ host ใช้ key ไหน

[groupname]
host1 ansible_private_key_file="/home/user/.ssh/privatekey1"
host2 ansible_private_key_file="/home/user/.ssh/privatekey2"

หรือถ้าทั้ง group ทุก server ใช้ key เดียวกันหมดก็สามารถกำหนดเป็น variable ของ group ก็ได้เช่นกัน เช่น

[groupname]
host1
host2
 
[groupname:vars]
ansible_private_key_file="/home/user/.ssh/privatekey1"

หรือหากเราต้องการใช้รหัสผ่านในการเชื่อมต่อ (ไม่แนะนำ) สามารถตั้งรหัสผ่านไว้ให้แต่ละ host ได้ (ย้ำว่าไม่แนะนำ อย่าหาทำ) ลักษณะดังนี้

[groupname]
host1 ansible_password="123456"
host2 ansible_password="abcdef"

เมื่อได้ inventory แล้วสามารถทดสอบการเชื่อมต่อไปหา host ปลายทางได้โดยใช้คำสั่งดังนี้

 ansible -i hosts -m ping all 

หากไม่สามารถเชื่อมต่อได้ ให้ลองเพิ่ม option -vvv ลงไปเพื่อดูว่า ansible เชื่อมต่อไปยัง server ปลายทางด้วย user/คำสั่งอะไรยังไง แล้วแก้ไขการตั้งค่าของ ansible ให้ถูกต้องอีกครั้ง

ansible จะใช้การตั้งค่าของ ssh client ตามที่เราตั้งค่าไว้ (~/.ssh/config) ดังนั้นสามารถตั้งค่าการเชื่อมต่อในนี้ก็ได้เช่นกัน