HA + Pi สั่งปิดเปิด Air Auto แทนเครื่อง MN-SERVER
คำสั่ง Restart service HA docker
- docker restart Docker-ID
ตั้งค่า Timezone ให้ Debian เพื่อให้เวลาตรง
- timedatectl set-timezone Asia/Bangkok
- timedatectl
- date
1. ติดตั้ง Home Assistant with Docker
https://intranet.scivalve.com/blog.php?u=281
2. HA และ PI สร้าง Private Key เพื่อให้ HA สามารถ SSH เข้าเครื่อง PI โดยไม่ต้องใช้รหัส
จะทำให้ HA สามารถเรียกคำสั่งโปรแกรมที่เครื่อง PI ได้
2.1. ใช้ Terminal ล็อคอินเข้าไปที่ เครื่อง Server ที่รัน Docker และ Home Assistant ของคุณ
2.2. รันคำสั่งนี้เพื่อสร้าง Key คู่ใหม่:
- ssh-keygen -t rsa -b 4096
2.3. ระบบจะถามคำถาม 2-3 ข้อ ให้ กด Enter ผ่านไปทั้งหมด เพื่อยอมรับค่าเริ่มต้น และ ไม่ต้องใส่รหัสผ่าน (passphrase) นะครับ
2.4. คำสั่งนี้จะสร้างไฟล์ id_rsa (กุญแจลับ) และ id_rsa.pub (กุญแจสาธารณะ) ขึ้นมาในโฟลเดอร์ .ssh ของ user ที่คุณกำลังล็อคอินอยู่บน Server (เช่น /root/.ssh/ หากคุณล็อคอินเป็น root)
[บนเครื่อง HA Server] ขั้นตอนที่ 2: คัดลอกเนื้อหา Public Key
2.5. รันคำสั่งนี้เพื่อแสดงเนื้อหาของ Public Key:
- cat ~/.ssh/id_rsa.pub
2.6. คัดลอก (Copy) ข้อความทั้งหมด ที่แสดงขึ้นมา มันจะขึ้นต้นด้วย ssh-rsa และลงท้ายด้วยชื่อ user@hostname ของคุณ
[บนเครื่อง Raspberry Pi] ขั้นตอนที่ 3: ติดตั้ง Public Key
2.7. ตอนนี้ ให้ใช้ Terminal ล็อคอินเข้าไปที่ เครื่อง Raspberry Pi ของคุณ
2.8. รันคำสั่งนี้เพื่อสร้างโฟลเดอร์และไฟล์ที่จำเป็น (หากยังไม่มี) และตั้งค่า permission ให้ถูกต้อง:
- mkdir -p ~/.ssh && chmod 700 ~/.ssh && touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys
2.9. เปิดไฟล์ authorized_keys ด้วยโปรแกรม nano:
- nano ~/.ssh/authorized_keys
2.10. วาง (Paste) Public Key ที่คุณคัดลอกมาจากขั้นตอนที่ 2 ลงในไฟล์นี้ จากนั้นกด Ctrl+X > กด Y > กด Enter เพื่อบันทึกและออกจากโปรแกรม
[บนเครื่อง HA Server] ขั้นตอนที่ 4: คัดลอก Private Key ให้ Docker
2.11. กลับมาที่ Terminal ของ เครื่อง HA Server
2.12. ตอนนี้ให้ทำขั้นตอนที่เราคุยกันล่าสุด คือคัดลอก Private Key (id_rsa) ที่เพิ่งสร้างในขั้นตอนที่ 1 ไปยังโฟลเดอร์คอนฟิกของ HA ที่แชร์กับ Docker
# แก้ /root/.ssh/id_rsa หาก Key ของคุณอยู่ที่อื่น
# แก้ /path/to/your/ha/config/ ให้เป็น Path จริง
เครื่องจริงอยู่ที่ /config/id_rsa
- cp /root/.ssh/id_rsa /path/to/your/ha/config/
2.13. ตั้งค่า permission ให้ไฟล์ที่คัดลอกไป:
- chmod 600 /path/to/your/ha/config/id_rsa
2.14. ทดสอบ SSH จากเครื่อง HA ไปเครื่อง PI จะต้องไม่ขึ้นถามรหัส Login
3. แก้ไข ไฟล์ configuration.yaml เพื่อให้มี Switch ปิดเปิด Air manual เพิ่ม
- ### Air ####
- shell_command:
- # !!! สำคัญ: แก้ไข <IP_ของ_RPi> และ path ไปยังไฟล์ .py ของคุณให้ถูกต้อง !!!
- #### TEST #####
- #air1_on: "sshpass -p 'XXX' ssh pi@192.168.0.3 'python3 /home/pi/Test.py 2302 4'"
- #air1_off: "sshpass -p 'XXX' ssh pi@192.168.0.3 'python3 /home/pi/Test.py 2302 4'"
- #air1_on: "ssh -i /HA/id_rsa -o 'StrictHostKeyChecking=no' pi@192.168.0.3 'python3 /home/pi/Test.py 2302 4'"
- #air1_off: "ssh -i /HA/id_rsa -o 'StrictHostKeyChecking=no' pi@192.168.0.3 'python3 /home/pi/Test.py 2302 4'"
- #air1_on: "ssh pi@192.168.0.3 'python3 /home/pi/Test.py 2302 4'"
- #air1_off: "ssh pi@192.168.0.3 'python3 /home/pi/Test.py 2302 4'"
- ##### Test Rocketchat #######
- #air1_on: "ssh -i /config/id_rsa -o 'StrictHostKeyChecking=no' pi@192.168.0.3 'python3 /home/pi/Test.py 2302 4'"
- #air1_off: "ssh -i /config/id_rsa -o 'StrictHostKeyChecking=no' pi@192.168.0.3 'python3 /home/pi/Test.py 2302 4'"
- air1_on: "ssh -i /config/id_rsa -o 'StrictHostKeyChecking=no' pi@192.168.0.3 'python3 /home/pi/On_air1.py'"
- air1_off: "ssh -i /config/id_rsa -o 'StrictHostKeyChecking=no' pi@192.168.0.3 'python3 /home/pi/Off_air1.py'"
- air2_on: "ssh -i /config/id_rsa -o 'StrictHostKeyChecking=no' pi@192.168.0.3 'python3 /home/pi/On_air2.py'"
- air2_off: "ssh -i /config/id_rsa -o 'StrictHostKeyChecking=no' pi@192.168.0.3 'python3 /home/pi/Off_air2.py'"
- # --- DEBUG COMMANDS ---
- #debug_find_path: "pwd > pwd.txt && ls -la >> pwd.txt"
- # New sensor for human-readable status
- sensor:
- - platform: template
- sensors:
- air_status:
- friendly_name: "สถานะแอร์"
- value_template: >-
- {% set state = states('input_select.active_air') %}
- {% if state == 'air1' %}
- Air 1 กำลังทำงาน
- {% elif state == 'air2' %}
- Air 2 กำลังทำงาน
- {% elif state == 'both' %}
- Air 1 และ Air 2 กำลังทำงาน
- {% else %}
- แอร์ทุกเครื่องปิดอยู่
- {% endif %}
- switch:
- - platform: template
- switches:
- air1_switch:
- friendly_name: "Air 1"
- value_template: "{{ states('input_select.active_air') in ['air1', 'both'] }}"
- turn_on:
- - service: shell_command.air1_on
- - service: input_select.select_option
- target:
- entity_id: input_select.active_air
- data:
- option: >-
- {% if is_state('input_select.active_air', 'air2') %}
- both
- {% else %}
- air1
- {% endif %}
- turn_off:
- - service: shell_command.air1_off
- - service: input_select.select_option
- target:
- entity_id: input_select.active_air
- data:
- option: >-
- {% if is_state('input_select.active_air', 'both') %}
- air2
- {% else %}
- none
- {% endif %}
- air2_switch:
- friendly_name: "Air 2"
- value_template: "{{ states('input_select.active_air') in ['air2', 'both'] }}"
- turn_on:
- - service: shell_command.air2_on
- - service: input_select.select_option
- target:
- entity_id: input_select.active_air
- data:
- option: >-
- {% if is_state('input_select.active_air', 'air1') %}
- both
- {% else %}
- air2
- {% endif %}
- turn_off:
- - service: shell_command.air2_off
- - service: input_select.select_option
- target:
- entity_id: input_select.active_air
- data:
- option: >-
- {% if is_state('input_select.active_air', 'both') %}
- air1
- {% else %}
- none
- {% endif %}
- # New switch for controlling both
- all_air_switch:
- friendly_name: "Air1+Air2"
- value_template: "{{ is_state('input_select.active_air', 'both') }}"
- turn_on:
- - service: shell_command.air1_on
- - service: shell_command.air2_on
- - service: input_select.select_option
- target:
- entity_id: input_select.active_air
- data:
- option: 'both'
- turn_off:
- - service: shell_command.air1_off
- - service: shell_command.air2_off
- - service: input_select.select_option
- target:
- entity_id: input_select.active_air
- data:
- option: 'none'
- input_select:
- active_air:
- name: Air ที่ทำงานอยู่
- options:
- - none
- - air1
- - air2
- - both # Added 'both' state
- initial: none
4. ไฟล์ automation.yaml เพื่อให้สลับ Air อัตโนมัติ เพิ่ม
- #### Off ก่อนแล้วค่อย ON เพื่อไม่ให้ Switch เปิดทั้ง 2
- #### delay 15 วิ รอให้ คำสั่งแรกทำงานเสร็จก่อน ถ้าทำต่อเลย คำสั่งแรกยังทำไม่เสร็จ Status จะไม่ถูก
- - id: air1_0000
- alias: "Air1 00:00 - 02:59"
- trigger:
- - platform: time
- at: "00:00:00"
- action:
- - service: switch.turn_off
- target:
- entity_id: switch.all_air_switch
- - delay: "00:00:15" # รอ 15 วินาที
- - service: switch.turn_on
- target:
- entity_id: switch.air1_switch
- - id: air2_0300
- alias: "Air2 03:00 - 05:59"
- trigger:
- - platform: time
- at: "03:00:00"
- action:
- - service: switch.turn_off
- target:
- entity_id: switch.all_air_switch
- - delay: "00:00:15" # รอ 15 วินาที
- - service: switch.turn_on
- target:
- entity_id: switch.air2_switch
- - id: air1_0600
- alias: "Air1 06:00 - 08:59"
- trigger:
- - platform: time
- at: "06:00:00"
- action:
- - service: switch.turn_off
- target:
- entity_id: switch.all_air_switch
- - delay: "00:00:15" # รอ 15 วินาที
- - service: switch.turn_on
- target:
- entity_id: switch.air1_switch
- - id: air2_0900
- alias: "Air2 09:00 - 11:59"
- trigger:
- - platform: time
- at: "09:00:00"
- action:
- - service: switch.turn_off
- target:
- entity_id: switch.all_air_switch
- - delay: "00:00:15" # รอ 15 วินาที
- - service: switch.turn_on
- target:
- entity_id: switch.air2_switch
- - id: air1_1200
- alias: "Air1 12:00 - 14:59"
- trigger:
- - platform: time
- at: "12:00:00"
- action:
- - service: switch.turn_off
- target:
- entity_id: switch.all_air_switch
- - delay: "00:00:15" # รอ 15 วินาที
- - service: switch.turn_on
- target:
- entity_id: switch.air1_switch
- - id: air2_1500
- alias: "Air2 15:00 - 17:59"
- trigger:
- - platform: time
- at: "15:00:00"
- action:
- - service: switch.turn_off
- target:
- entity_id: switch.all_air_switch
- - delay: "00:00:15" # รอ 15 วินาที
- - service: switch.turn_on
- target:
- entity_id: switch.air2_switch
- - id: air1_1800
- alias: "Air1 18:00 - 20:59"
- trigger:
- - platform: time
- at: "18:00:00"
- action:
- - service: switch.turn_off
- target:
- entity_id: switch.all_air_switch
- - delay: "00:00:15" # รอ 15 วินาที
- - service: switch.turn_on
- target:
- entity_id: switch.air1_switch
- - id: air2_2100
- alias: "Air2 21:00 - 23:59"
- trigger:
- - platform: time
- at: "21:00:00"
- action:
- - service: switch.turn_off
- target:
- entity_id: switch.all_air_switch
- - delay: "00:00:15" # รอ 15 วินาที
- - service: switch.turn_on
- target:
- entity_id: switch.air2_switch
5. ที่เครื่อง PI เพิ่มไฟล์ 4 ไฟล์ สำหรับ HA เรียกใช้
On_air1.py, On_air2.py, Off_air1.py, Off_air2.py
Code
On_air1.py
- #!/usr/bin/python3
- import time
- import RPi.GPIO as GPIO
- GPIO.setmode(GPIO.BCM)
- GPIO.setwarnings(False)
- GPIO.setup(20, GPIO.OUT)
- GPIO.output(20, GPIO.LOW)
- #GPIO.setup(21, GPIO.OUT)
- #GPIO.output(21, GPIO.LOW)
- ##### Rocketchat #####
- import requests
- from datetime import datetime
- def send_to_rocketchat(message):
- url = "http://xx.xx.xx.xx:3000/api/v1/chat.postMessage"
- headers = {
- "Content-type": "application/json",
- "X-Auth-Token": "XXX",
- "X-User-Id": "XXX"
- }
- payload = {
- "channel": "#Test", #IT_Notification
- "text": message
- }
- response = requests.post(url, json=payload, headers=headers)
- message = "แจ้งเตือน เปิด AIR1 ปิด AIR2 " + datetime.today().strftime('%Y-%m-%d %H:%M:%S')
- send_to_rocketchat(message)
On_air2.py
- #!/usr/bin/python3
- import time
- import RPi.GPIO as GPIO
- GPIO.setmode(GPIO.BCM)
- GPIO.setwarnings(False)
- #GPIO.setup(20, GPIO.OUT)
- #GPIO.output(20, GPIO.LOW)
- GPIO.setup(21, GPIO.OUT)
- GPIO.output(21, GPIO.LOW)
- ##### Rocketchat #####
- import requests
- from datetime import datetime
- def send_to_rocketchat(message):
- url = "http://192.168.2.76:3000/api/v1/chat.postMessage"
- headers = {
- "Content-type": "application/json",
- "X-Auth-Token": "XXX",
- "X-User-Id": "XXX"
- }
- payload = {
- "channel": "#Test", #IT_Notification
- "text": message
- }
- response = requests.post(url, json=payload, headers=headers)
- message = "แจ้งเตือน เปิด AIR2 ปิด AIR1 " + datetime.today().strftime('%Y-%m-%d %H:%M:%S')
- send_to_rocketchat(message)
Off_air1.py
- import time
- import RPi.GPIO as GPIO
- GPIO.setmode(GPIO.BCM)
- GPIO.setwarnings(False)
- GPIO.setup(20, GPIO.OUT)
- GPIO.output(20, GPIO.HIGH)
- #GPIO.setup(21, GPIO.OUT)
- #GPIO.output(21, GPIO.HIGH)
- GPIO.cleanup()
Off_air2.py
- #!/usr/bin/python3
- import time
- import RPi.GPIO as GPIO
- GPIO.setmode(GPIO.BCM)
- GPIO.setwarnings(False)
- #GPIO.setup(20, GPIO.OUT)
- #GPIO.output(20, GPIO.HIGH)
- GPIO.setup(21, GPIO.OUT)
- GPIO.output(21, GPIO.HIGH)
- GPIO.cleanup()
No comments:
Post a Comment