Näytetään tekstit, joissa on tunniste Glue. Näytä kaikki tekstit
Näytetään tekstit, joissa on tunniste Glue. Näytä kaikki tekstit

tiistai 20. heinäkuuta 2021

Home Assistant - Gluehome lukkointegraatio

Olen aikaisemmin Glue-lukko aihetta jo sivunnut aikaisemmassa blogipostauksessa: http://omakotikotitalomme.blogspot.com/2019/09/glue-alylukon-rajapintojen-tutkimista.html

Keväällä Glue muutti mobile applikaation kokonaan ja vanha API urli lakkasi toimimasta. Nopeasti vilkaisin uuden appiksenkin sisälle, ja siellä oli https://mobile.gluehome.com/api/ urli käytössä ja nopealla vilkaisulla löytyi myös mqtt broker hittejä, mikä olisi lupaavaa, jos semmoinen rajapinta joskus aukeasti. Mutta onneksi ei tarvinnut ruveta tarkemmin appiksen toimintaa penkomaan, kun internetistä löytyi uusi semi-official API, kun Homebridgelle oli julkaistu toteutus GlueHome integraatioille (https://github.com/GlueHome/homebridge-plugin). Joten samaa esimerkkiä seuraten voi curlilla tai HA:lla lukkoa komentaa.

# Create/fetch <api-key> with glue <user>:<pass> according to plugin README:
curl --location --request POST 'https://user-api.gluehome.com/v1/api-keys' --header 'Content-Type: application/json' -u <user>:<pass> --data-raw '{ "name": "My Test Key", "scopes": ["events.read", "locks.read", "locks.write"] }'

# Fetch <lock-id> using created <api-key>
curl -L -H "Authorization: Api-Key <api-key>"  https://user-api.gluehome.com/v1/locks

# Test operation <op>=lock/unlock on <lock-id> 
curl -L -H "Authorization: Api-Key <api-key>" -H 'Content-Type: application/json' -d '{"type":"<op>"}' https://user-api.gluehome.com/v1/locks/<lock-id>/operations

Ylläolevilla esimerkeillä voi testata lukon toimintaa, ja samalla voit luoda/pistää talteen oman lukkosi <api-key> ja <lock-id>. Niitä tarvitaan Home Assistantin puolella kun editoit secrets.yaml ja configuration.yaml tiedostoja.

secrets.yaml:
glue_command_on: "curl -L -H 'Authorization: Api-Key ApiAvain' -H 'Content-Type: application/json' -d '{\"type\":\"lock\"}' https://user-api.gluehome.com/v1/locks/123456/operations"
glue_command_off: "curl -L -H 'Authorization: Api-Key ApiAvain' -H 'Content-Type: application/json' -d '{\"type\":\"unlock\"}' https://user-api.gluehome.com/v1/locks/123456/operations"

glue_resource: "https://user-api.gluehome.com/v1/locks/123456"
glue_auth: "Api-Key ApiAvain"
configuration.yaml:
switch varasto_ovi:
- platform: command_line
  switches:
    varasto_lock:
      friendly_name: "Varaston Ovi"
      command_on: !secret glue_command_on
      command_off: !secret glue_command_off
sensor varaston_ovi:
  - platform: rest
    resource: !secret glue_resource
    name: varasto_lock
    method: GET
    headers:
      Authorization: !secret glue_auth
    json_attributes:
      - description
      - batteryStatus
      - lastLockEvent
      - connectionStatus
    value_template: "OK"
  - platform: template
    sensors:
      varasto_lock_last_event:
        friendly_name: Last varasto lock event
        value_template: "{{ state_attr('sensor.varasto_lock', 'lastLockEvent')['eventType'] }}"
      varasto_lock_battery:
        friendly_name: Varasto lock battery
        value_template: "{{ state_attr('sensor.varasto_lock', 'batteryStatus') }}"

Ja HA:n restartin jälkeen lukko löytyy switchinä Overview näytöllä;



tiistai 3. syyskuuta 2019

Glue älylukon rajapintojen tutkimista

Tuossa aikaisemmassa postauksessa https://omakotikotitalomme.blogspot.com/2019/09/etaohjattava-glue-alylukko-wifi.html asensimme älylukon varaston oveen. Ja siinä vähän harmittelin, ettei valmistaja ole virallisesti julkaissut avointa REST API:a, että voisin itse ohjelmallisesti lukkoa ohjata.

Ekana tiputamme Glue lukon Android paketin https://www.apkdecompilers.com/-sivustolle, ja katsomme löytyykö paketin sisältä jotain mielenkiintoista, jolla voisimme saavuttaa yhteensopivuuden curl ohjelman kanssa.
root@jonni-ThinkPad-W520:~/gluehome# grep https.*api apk/*
Binääritiedosto apk/resources\classes.dex täsmää hakuun
apk/sources\com\gluehome\gluecontrol\h.java:        aVar.a("https://api.gluehome.com/api/");
apk/sources\com\gluehome\gluecontrol\h.java:        return new g.s.a().a("https").d(str.equals("dev") ? "gluebackend-tc-api.azurewebsites.net" : "api.gluehome.com").e("api").e(CoreConstants.EMPTY_STRING).c();
apk/sources\com\google\android\gms\b\ix.java:        return new Builder().scheme("https").path("//pagead2.googlesyndication.com/pagead/gen_204").appendQueryParameter("id", "gmob-apps-report-exception").appendQueryParameter("os", VERSION.RELEASE).appendQueryParameter("api", String.valueOf(VERSION.SDK_INT)).appendQueryParameter("device", u.e().e()).appendQueryParameter("js", this.f8897e.f7780b).appendQueryParameter("appid", this.f8896d).appendQueryParameter("exceptiontype", cls.getName()).appendQueryParameter("stacktrace", stringWriter.toString()).appendQueryParameter("eids", TextUtils.join(",", df.a())).appendQueryParameter("exceptionkey", str).appendQueryParameter("cl", "135396225").appendQueryParameter("rc", "dev").toString();

Bingo, tuolta löytyi, että Android applikaatio käyttää API rajapintanaan "https://api.gluehome.com/api/", ja näyttääpä heillä jääneen kehitysympäristön azure sitekin koodiin mukaan, mutta sen voimme ignoroida. Joten keskitymme tuon api urlin service rajapinnan tutkimiseen. Ja sitten seuraava luova greppi:
root@jonni-ThinkPad-W520:~/gluehome# grep "o(a = \"" apk/*Service*
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "UserRegistrations/{id}/Activate")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "AutoUnlocks")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "AutoUnlocks")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Invites")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Hubs/{id}/Commands")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Accesses")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Accesses")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "AccessSchedules/")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "AccessSchedules/")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Locations/{id}/Images")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Locations/{id}/Images")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Locations")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Locations")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "LockEvents")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "LockEvents")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Locks")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Locks")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Hubs/{id}/PairLocks")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Hubs")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "RegisterPushNotifications")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "RegisterPushNotifications")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "UserRegistrations")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "UserRegistrations")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "SecurityQuestions")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "SecurityQuestions")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Users/{id}/Icons")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Users/{id}/Icons")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Users/{id}/modifications")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Users/{id}/modifications")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "ResetPassword")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "ResetPassword")
apk/sources\com\gluehome\backend\glue\GlueHomeService.java:    @o(a = "Validate")

Ja tuossahan lista sitten majaileekin, ja sorsaa tutkimalla näkee syökö se sisäänsä GET, PUT tai DELETE metodia. Ja tavoitteenahan meillä on curlilla päästä ovea lukitsemaan ja avaamaan. Sitten luomme loginpass-tiedoston:
machine api.gluehome.com login <email> password <password>

Ja yläpuolella olevaa apilistaa kun luemme, niin kaipaamme lukon tai hubin {id} arvoa aika monessa tapauksessa. Ja kaikki wifi hubissa olevat linkatut lukot saamme /Hubs-komennolla (GET).
root@jonni-ThinkPad-W520:~/gluehome# curl -k -H "Content-type: application/json" --netrc-file loginpass https://api.gluehome.com/api/Hubs/ | jq '.[].LockIds[], .[].Id'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   405  100   405    0     0    605      0 --:--:-- --:--:-- --:--:--   639
"11111111-1111-1111-1111-111111111111"
"22222222-2222-2222-2222-222222222222"

Ja sieltähän lukon id pullahti (lukko vaihdettu ykköseksi ja hubi id kakkoseksi ihan vaan blogin takia). Ja tuon tiedon perusteella voimme jo lukkoa ohjata "Hubs/{id}/Commands"-APIlla. Tai kaivetaan vielä tuosta rajapinnasta tiedot ulos.
root@jonni-ThinkPad-W520:~/gluehome# cat "apk/sources\com\gluehome\backend\glue\HubCommandRequest.java"
package com.gluehome.backend.glue;

import com.google.a.a.c;
import java.util.UUID;

public class HubCommandRequest {
    public static int COMMAND_HUB_FIRMWARE_UPDATE = 4;
    public static int COMMAND_LOCK = 1;
    public static int COMMAND_LOCK_FIRMWARE_UPDATE = 3;
    public static int COMMAND_UNLOCK = 0;
    public static int LOCK_COMMAND_LIST_UPDATE = 5;
    @c(a = "HubCommand")
    public int hubCommand;
    @c(a = "LockId")
    public UUID lockId;
}

Tuosta näkee, että kyseinen API haluaa POST komenossa formissa kaksi parametriä, toinen on "LockId", johon siis pistämme juuri kaivamamme lukon ID:n ja toisena parametrinä "HubCommand", joka on INT. Ja intin arvot onkin tuossa mukavasti listattuna samalla, eli nollalla unlock ja ykkösellä lukkoon. Ja isommat numerot on sitten firmiksen päivityksia yms(joita emme tarvitse).

Ja sitten onko lopullisen testin aika, syötämme curlille formin tuossa muodossa ja katsotaan mitä tapahtuu.

root@jonni-ThinkPad-W520:~/gluehome# curl -X POST -k -d "LockId=11111111-1111-1111-1111-111111111111&HubCommand=0" --netrc-file loginpass https://api.gluehome.com/api/Hubs/22222222-2222-2222-2222-222222222222/Commands | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   112  100    56  100    56     80     80 --:--:-- --:--:-- --:--:--   160
{
    "Id": "6550eb7c-5fe6-4dc5-8f0d-2dccaa077777",
    "Status": 1
}
root@jonni-ThinkPad-W520:~/gluehome# curl -X POST -k -d "LockId=11111111-1111-1111-1111-111111111111&HubCommand=1" --netrc-file loginpass https://api.gluehome.com/api/Hubs/22222222-2222-2222-2222-222222222222/Commands | python -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   112  100    56  100    56     98     98 --:--:-- --:--:-- --:--:--   197
{
    "Id": "a95c39ec-2c47-4a0c-bb7b-3f154a4e00000",
    "Status": 1
}

Ja siellähän se möllöttää, varaston ovi aukesi ja meni kiinni, ja Androidiin tuli notifikaatiot myös komentojen onnistumisesta.

Disclaimerina, että koska REST API ei ole virallisesti avoin, niin valmistaja voi softapäivitysten välillä vapaasti sitä muutella, mutta "works for me". Samalla tässä pitää painottaa sen tärkeyttä, kun rekisteröitte tuota lukkoa, niin keksikää mahdollisimman pitkä ja uniikki salasana käyttäjätunnukselle, sillä jos se vuotaa, niin kuka vaan saa ovesi auki tietämällä sähköpostin ja salasanan. Muuten tuolla ei nyt nopealla silmäyksellä mitään hälyttävää löytynyt, datakanava on https:n päällä, joten siltä osin turvallinen. Näytti tuolla olevan muutama google mainos ja statistiikka urli, joten softa saattaa kerätä statistiikka, mutta niinpä taitaa about kaikki applikaatiot tehdä.

Niin ja tässä postauksessa ei mitään maailmaa mullistavaa uutta tietoa. Näin jälkeenpäin kun googlettaa, niin Gluehomen gitistä löytyy samat tiedot sorsia lukemalla, josta näkee esim, että miten betterylevel lasketaan (https://github.com/GlueHome/homebridge-glue/blob/master/index.js) ja hyvää lisälukemista aiheesta: Security evaluation of smart door locks.

Nyt kun pystyn REST rajapintaa itse kutsumaan, niin saatan tulevaisuudessa hankkia toisenkin lukon talon puolelle.

EDIT: Vanha API lakkasi toimimasta, mutta tässä on uudempi blogipostaus jossa uutta API:a hyödynnetään: http://omakotikotitalomme.blogspot.com/2021/07/home-assistant-gluehome-lukkointegraatio.html