Home Assistant ir žinutės per Facebook Messenger

Posted: 2019-02-07 in Darbeliai
Žymos:,

Panorau ant savo Home Assistant padaryti kokį nors pranešimų pristatymą. Na, kad pvz. temperatūros Node’ui nulūžus gaučiau apie tai žinutę, ar jei koks šviesų valdiklis iš rikiuotės išeitų. Arba kad ryte žinutę parašytų su temperatūra lauke.

Paprasčiausia pasikurti šitą reikalą per emailus. T.y. automatizacija išsiunčia emailą nurodytu adresu ir tuo reikalas baigiasi.

Deja, aš telefone emailus skaitau labai retai, sinchronizaciją laikau išjungtą. Pasitikrinu tik tada, kai man užeina. Čia toks senamadiškas požiūris į reikalą, o ir biškį baterijos sutaupo. Ant senesnių telefonų tai sutaupydavo labai normaliai.

Užtat Messenger Lite pas mane yra viena iš pagrindinių komunikacijos priemonių. Sugalvojau pasikurti Messengerio botą, kuris man galėtų žinelę parašyt.

Iššūkis pasitaikė nemažas, surijęs geras tris valandas mano galvos skausmo. Kad sutaupyčiau jūsų laiko, papasakosiu viską nuo pradžių.

Pasiruošimai

Pirmiausia reikės susikurti feisbuke puslapį. Kokį norit, nesvarbu. Jis galės būti nepublikuotas. Publikuoti reikės trumpam, bet apie tai toliau.

Antra, susikurti Facebook Developer paskyrą.

Trečia, appsų dashboarde susikurti Facebook App.

Ketvirta, prie appso produktų prisidėti Messenger.

Pradinė konfigūracija

Dabar reikia atsidaryti savo appsą ir ten susieti jį su savo sukurtu puslapiu bei gauti to puslapio prieigos tokeną:

Facebook App puslapio tokenas | Darau, blė

Galime pasiredaguoti Home Assistant konfigūraciją ir prisidėti Messenger žinučių platformą:

notify:
  - name: FACEBOOK
    platform: facebook
    page_access_token: ABRAKADABRAABRAKADABRAKADABRABRABRABRAKADA

Ir čia galėtų būti viskas, bet…

Kaip dabar išsiųsti žinutę? Reikia susikurti automatizaciją, kurią kas nors triggerintų. Ir tada suformuoti žinutę bei išsiųsti. Išsiųsti kam? Home Assistant vadovėlyje parašyta, kad reikia nurodyti gavėjo telefono numerį. Aš net pasitikrinau, ar turiu tą numerį įsirašęs. Su aštuonetu priekyje. Pabandžiau įvesti per naują — Facebookas pats suformatuoja vietinį numerį. Nu ok, bandom.

Neina. Iššoka kažkoks keistas erroras loguose:

Access to the Customer Matching API and Customer Matching via the Send API is currently available in limited release. To learn more, contact your Facebook partner manager or representative.

Nu, čia kaip ir aišku. Yra tokia fyčia Messengerio platformoje, leidžianti susieti žinomą telefono numerį su konkrečiu asmeniu ir jam žinutę išsiųsti. Bet toliau skaitome ir randame įdomybių…

Labai sunervino tai, kad būčiau galėjęs viską užsikurti per pusvalanduką, jei ne idiotiškas apribojimas, jog puslapio administratorius turi būti su amerikonišku telefono numeriu. Kitoms šalims telefono numerio ir asmens susiejimas neleidžiamas. Bbž kodėl. O Amerikoj išvystytas reikalas, kad Messengerio botai siuntinėtų pranešimus, jei klientas telefono numerį paliko. Atsilikimas kitur pasauly 😦

Kai jau puslapio adminas yra amerikonas, tai siuntinėti žinutes gali visiems. Būčiau radęs aš draugų Amerikoj, bet nelabai norėjau terliotis ir nusprendžiau eiti kiek sudėtingesniu keliu, bet susitvarkyti pats.

PSID ir Webhook

Visiškai be apribojimų robotukai gali siuntinėti žinutes Facebook vartotojams, jei yra žinomas jų PSID.

Disclaimer: jei jūs kuriatės appsą tik šitam pasižaidimui ir tas appsas nebus publikuotas (nes jis turėtų kažką ir nuveikti bei būti Facebook patvirtintas viešinimui), tai žinutes galėsite siuntinėti tik savo puslapio administratoriams bei appso administratoriams irba testuotojams. Namų poreikiams, be abejo, to užtenka.

PSID iššifruojamas kaip „Page-Scoped ID“. Kitaip sakant, šitas ID yra sukuriamas Facebook asmeniui, kai jis parašo žinutę puslapiui. Kaip žinote (arba gal ne), puslapių paskyros su asmenų paskyromis negali inicijuoti pokalbių — tik atsakyti į jas. Negalima puslapio vardu asmeninių įrašų komentuoti ir pan. Komunikacijos pradžia yra vienpusė, inicijuojama asmeninės paskyros. Taigi tas PSID sukuriamas pirmosios žinutės metu ir tada puslapio paskyra per jį komunikuoja su asmenine paskyra „atgal“.

Taigi aišku, kad reikės savo puslapiui parašyti žinutę 🙂 Jei puslapis nepublikuotas, tą galima padaryti įsijungus peržiūrą, kaip jis „atrodys kitiems vartotojams“. O jei publikuotas, tai Messengeryje galima savo puslapio pavadinimą įrašyti, susirasti ir parašyti žinutę.

Cha, bet per Feisbuko puslapį ar messengerį to PSID neišsikapstysite 🙂 Aš bandžiau, užuominų į jį niekur neradau, nors ir labai stengiausi perkratyti. Galbūt gerai užslėptas ar užhashintas.

Tačiau ne viskas prarasta 🙂 Kadangi turime appsą, galime jam susikurti Webhooką. Kitaip sakant, susilipdyti tikrą robotą, jei norime. Principas toks: užregistruojam URL ir užsižymim, kokiems įvykiams atsitikus į tą URL bus atsiųsta POST užklausa. Mums reikia messages įvykio. Tada kai tik koks nors asmuo parašys mūsų puslapiui žinutę, į nurodytą URL ateis POST užklausa su visomis žinutės smulkmenomis. Ten bus ir mums taip reikalingas PSID.

Apie Webhook iš eilės. Mums reikia Web serverio ir kad jis atsakytų į pirmąją registracijos GET užklausą, o paskui sėkmingai apdorotų POST užklausas. Ir, deja, nuo šito reikia pradėti. Yra pavyzdėlių su Node.js, bet aš užsikūriau paprastutį Python serverėlį (kažkada rašiau apie patį paprasčiausią Python Web serverį). Pridėjau GET ir POST apdorojimo rutinas. Pradžiai tik išspjaunančias viską, kas ateina: headerius, POST turinį ir pan.

Susidūriau su pirma problema: Facebook leidžia tiktai HTTPS užklausas. Aš ne prieš, čia viskas ok. Pasiforwardinau laukan portą ir įvedžiau savo namų IP. O Facebookas sako: soriukas, šitas Webhookas turi self-signed sertifikatą, eik nx. Čia jau truputuką susinervinau, galėtų visgi appsams developmento stadijoj ir self-signed leisti… bet neleidžia. Kur gauti URLą su normaliu sertifikatu?

O čia mums gali padėti NGROK. Tai kaip tik tokia paslauga vargšams biedniems developeriams, kurie neturi savo Web serverių su autoritetingais sertifikatais.

NGROK veikia kaip proksis, nereikia jokių portų forwardintis. Pasileidžiate savo serverėlį ant kompo, pasileidžiate NGROK su serverėlio portu ir gaunate URL. Serverėlis gali būti ir HTTP — NGROK „apvelka“ jį su HTTPS, taigi dar mažiau vargo:

Facebook Messenger Webhook Python 3 | Darau, blė

Čia aš su CURL testavausi savo vargšą serverėlį, nes su NGROK gavęs URL bandžiau Webhooką prisiregistruot. Nežinojau, ką atgal reik pasiųst. O atgal reikia pasiųst hub.challenge parametrą.

Know how: nemokama NGROK versija kaskart paleidus NGROK proksį sugeneruoja naują nuorodą. Mokama paskyra rimtesniems developeriams leidžia turėti pastovius URL.

Taigi pasileidžiam mano Python Web serverėlį (kodas bus pabaigoj), pasileidžiam NGROK kitam lange, pabandom su gautu URL’u testavimui tokią CURL komandą:

curl -X GET https://ec632f03.ngrok.io?hub.mode=subscribe\&hub.challenge=380621492\&hub.verify_token=blahaha

Jei Python serverėlis išspjauna aukščiau matomą outputą — viskas ok, galime eiti į savo appsą ir registruotis Webhooką:

Facebook Messenger Webhook Setup | Darau, blė

Paspaudžiam „Setup Webhooks“, įvedam savo URL, „blahaha“ už verificationą (nes toks pas mane įhardkodintas), užžymim „messages“ ir spaudžiam „Verify and Save“:

Facebook Messenger Webhook Setup | Darau, blė

Jei viskas suvaikščiojo — šitas langas išnyksta ir jau turim veikiantį Webhooką. Tik NGROK nesustabdykite dabar, nes URL pasikeis ir reikės Webhooką per naują registruotis 🙂

Dabar dar pascrollinam žemyn ir priregistruojam („Subscribe“) Webhookus konkrečiam puslapiui:

Facebook Messenger Webhook susiejimas su puslapio paskyra | Darau, blė

Štai ir viskas šioje ilgoje dalyje. Dabar einam į feisbuką ir rašom savo puslapiui žinutę. Pasiuntus žinutę mano serverėlis į konsolę išspjauna atėjusios žinutės turinį:

Facebook žinutė atėjusi į Messenger Webhook | Darau, blė

Šitą JSONą galim nusikopijuoti ir susiformatuoti geresniam skaitymui. Mums svarbiausias dalykas yra „sender“ ir jo „id“ — tai ir yra tas reikalingasis PSID:

{
  "entry": [
    {
      "id": "<Žinutės ID>",
      "messaging": [
        {
          "message": {
            "mid": "VZsQNnavOXDr9_3mx0sgY_qBTBPmlGFhMkKp0tiU9cUy3M3DN96payoaNtPqVl9hFGwn2cvZrEZpNyzRo8s-oQ",
            "seq": 276943,
            "text": "labas rytas"
          },
          "recipient": {
            "id": "<Gavėjo, t.y. puslapio ID>"
          },
          "sender": {
            "id": "<Mums taip reikalingas PSID!!!>"
          },
          "timestamp": 1549375702983
        }
      ],
      "time": 1549375704175
    }
  ],
  "object": "page"
}

Tai va. Turime savo asmeninės paskyros PSID ir dabar galim pradėt setupint Home Assistant, kad mums per messengerį mūsų susikurto puslapio vardu siuntinėtų mums žinutes.

Šitoj vietoj dar reikėtų unsubscribinti savo puslapį nuo Webhookų — jei netyčia parašysim savo puslapiui žinutę, tai bent jau appsas nesikeiks.

Tiesa, jei galite sau leisti turėti serverį su nuolatiniu URL, tai galite pasidaryti automatizaciją ir valdyti savo išmanų namą rašinėdami žinutes per Messengerį 🙂 Bet tai visai ne šios rašliavos tema.

Detalesnė Home Assistant konfigūracija

Taigi kaip susikurti Messenger žinučių platformos konfigūraciją, rašiau aukščiau. O dabar reikia kokios nors automatizacijos, kuri išsiųstų žinutę turimam PSID.

Vienas pavyzdukas, kuris praneša man, kai nuo WiFi atsijungia temperatūrų sekimo valdiklis:

- id: temp_node_down
  alias: "T-node down"
  trigger:
    - platform: state
      entity_id: binary_sensor.tnode
      from: "on"
      to: "off"
  action:
    - service: notify.FACEBOOK
      data:
        message: "Nėra ryšio su temperatūra!"
        target:
          - '<Mano PSID>'

Dabar valdikliui atsijungus gaunu žinutę.

Kita žinutė man ir mano žmonai parašanti ryte lauko temperatūrą:

- id: labas_rytas
  alias: "Labas rytas"
  trigger:
    - platform: time
      at: '07:10:00'
    - platform: time
      at: '10:00:00'
  condition:
  - condition: or
    conditions:
    - condition: time
      after: '07:10:00'
      before: '07:12:00'
      weekday:
        - mon
        - tue
        - wed
        - thu
        - fri
    - condition: time
      after: '10:00:00'
      weekday:
        - sat
        - sun
  action:
    - service: notify.FACEBOOK
      data:
        message: "Labas rytas! Šiandien temperatūra lauke {{ states('sensor.laukas') }}."
        target:
          - '<Mano PSID>'
          - '<Žmonos PSID>'

Prasitestuojam žinutes. Valio, jos į Messengerį ateina:

Home Assistant žinutės į Facebook Messengerį | Darau, blė

Dar sugalvojau, kad reikia rytinę žinutę padaryt linksmesnę ir įterpt kokią nors motyvuojančią citatą 😀 Pasirašiau paprastą Python skriptuką, kuris iš kabutes.kasvyksta.lt ištraukia atsitiktinę citatą. O Home Assistant yra galimybė susikonfigūruoti sensorių, kurio reikšmė imama iš shell komandos:

- platform: command_line
  name: Dienos citata
  command: "python3 /config/dienoscitata.py"
  scan_interval: 60000
  command_timeout: 30

Kad „sensoriaus“ reikšmė nebūtų per dažnai keičiama, bet visgi kartą per parą tikrai pasikeistų, tai scan_interval sekundėmis padariau trumpesnį, nei parą, bet nebaisiai.

Štai, perstartavus Home Assistant atsirado naujas „sensorius“:

Home Assistant command_line sensorius — Dienos citata | Darau, blė

Reikia pasiredaguot labo ryto automatizaciją, kad šitą citatą kartu išsiųstų. Paredaguojam žinutės formatą:

        message: "Labas rytas! Šiandien temperatūra lauke {{ states('sensor.laukas') }}.\nDienos citata:\n {{ states('sensor.dienos_citata') }}"

Prasitestuojam, į telefoną žinutė atkeliavo:

Home Assistant žinutė per Facebook Messenger su dienos citata | Darau, blė

Štai ir viskas, namų robotukas gali rašinėti žinutes 🙂

Kodas

Web serverėlio kodas, kuris padeda užsikurti Webhooką ir leidžia išsikrapštyti PSID:

# -*- coding: utf-8 -*-
'''
Created on 2019-02-05

@author: Darau, blė
'''
import sys
import os
from urllib.parse import urlparse, parse_qs

from http import server

class MyHandler(server.SimpleHTTPRequestHandler):
    def resp(self):
        self.protocol_version='HTTP/1.1'
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_POST(self):
        print("------------ POST -------------")
        print(self.path)
        print(self.headers)

        query_components = parse_qs(urlparse(self.path).query)
        for q in query_components:
            print(q, ":", query_components[q])

        content_length = int(self.headers['Content-Length'])
        post_data = self.rfile.read(content_length)
        print(post_data.decode('utf-8'))

        self.resp()
        self.wfile.write("Bla!".encode("utf-8"))
        return

    def do_GET(self):
        print("------------ GET -------------")
        print(self.path)
        print(self.headers)

        query_components = parse_qs(urlparse(self.path).query)

        if "hub.mode" in query_components and query_components["hub.mode"][0] == "subscribe" and query_components["hub.verify_token"][0] == "blahaha":
            print("SUBSCRIBE!")
            self.resp()
            self.wfile.write(query_components["hub.challenge"][0].encode("utf-8"))
        else:
            self.send_response(403)

        return

httpd_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
if len(sys.argv) > 1 and sys.argv[1].isdigit():
    p = int(sys.argv[1])
else:
    p = 4443

httpd = server.HTTPServer(('0.0.0.0', p), MyHandler)
httpd.serve_forever()

Dienos citatos „sensoriaus“ kodas:

# -*- coding: utf-8 -*-
'''
Created on 2019-02-06

@author: Darau, blė
'''
from lxml import etree
import http.client

connection = http.client.HTTPSConnection("kabutes.kasvyksta.lt", timeout=30)
connection.request('GET', '/')

response = connection.getresponse()

if response.status != 200:
    print ("Šiandien citatos nebus!")
    exit(-1)

content = response.read().decode('utf-8')
tree = etree.HTML(content)

cit = tree.xpath("//div[@class='citatos-blokas']/div[@class='citata']")
aut = tree.xpath("//div[@class='citatos-blokas']/div[@class='citatos-apacia']/div[@class='autorius']/a")

print("%s (- %s)" % (cit[0].text, aut[0].text))

exit(0)

Štai ir viskas. Galite pasitobulinti savo Home Assistant ir duoti jam Facebook Messenger prieigą.

Komentarai
  1. HDi parašė:

    Kažkaip atrodo labai sudėtinga ir daug vargo kur nereikia. O nebūtų buvę paprasčiau MQTT išnaudoti ir kokį android/iOS clientą užsubscribint?

    • Darau, Blė parašė:

      Iš principo paprasčiau, tačiau reikėtų tada savo MQTT publikuot. O mano tikslas buvo išnaudoti kokį nors viešą servisą. Yra ir kitų variantų, pavyzdžiui Pushbullet — tačiau tai būtų dar vienas appsas telefone. Kaip minėjau, mano atveju Messenger yra pagrindinė komunikacijos priemonė, tad jį išnaudoti man asmeniškai yra parankiausia.

      Vargas vienkartinis, o norintiems pakopijuoti bus paprasčiau 🙂

    • Darau, Blė parašė:

      Ai, dar Wife Acceptance Factor’iaus reikia nepamiršti 🙂

  2. Karolis parašė:

    Dėkui už įrašą. NGROK pasirodė įdomus įrankis tokiems vienetiniams webhookams.
    Vis gi, jei prireiktų pastovaus urlo nemokamai: galima gauti domainą iš https://www.duckdns.org ir validų ne self-signed sertifikatą iš https://letsencrypt.org .

  3. Dainius parašė:

    Kažkaip čia man viskas over engineered.

    Aš asmeniškai kas ryta siunčiu laiškus:

    O “greitoms žinutėms“ naudoju savo rašyta android aplikaciją su push notifications. Aplikacija iš principo gali nieko nedaryti, o tik gauti notificatuon’us. Gal tokių apps’ų ir jau ir yra pridaryta, bet aš norėjau pats pramokti šito meno 🙂

    Iš serverio pusės reikia tik pa’curl’int tam tikra URL su tam tikru raktu ir žinute. Paprasčiau nebūna.

    PS: radau sena screen’ą kaip telefone atrodo https://i.postimg.cc/vHvSK1cd/Screenshot-20181121-214248.png

  4. Aurelijus parašė:

    Sveikas norėjau paklausit kaip tavo raja gyvena 😉 kokios sanaudos gavosi sausio mėn.

Parašykite komentarą

Įveskite savo duomenis žemiau arba prisijunkite per socialinį tinklą:

WordPress.com Logo

Jūs komentuojate naudodamiesi savo WordPress.com paskyra. Atsijungti /  Pakeisti )

Google photo

Jūs komentuojate naudodamiesi savo Google paskyra. Atsijungti /  Pakeisti )

Twitter picture

Jūs komentuojate naudodamiesi savo Twitter paskyra. Atsijungti /  Pakeisti )

Facebook photo

Jūs komentuojate naudodamiesi savo Facebook paskyra. Atsijungti /  Pakeisti )

Connecting to %s

Brukalų kiekiui sumažinti šis tinklalapis naudoja Akismet. Sužinokite, kaip apdorojami Jūsų komentarų duomenys.