Cleverio smart fjärrströmbrytare utan koppling till molnet
Jag köpte nyligen en Wi-Fi uppkopplad fjärrströmbrytare från Cleverio, som säljs av Kjell & Co, mer specifikt den här modellen.
Följer man de officiella instruktionerna får man tips om en app att använda för att styra och parkoppla strömbrytaren.
Efter lite nyfikenhet förstod jag att produkterna från Cleverio i själva verket är omprofilerade Tuya-enheter, vilket signifikant ökar möjligheterna för att hitta andra som undersökt produkterna närmare.
Jag har två mål:
- Kunna styra strömbrytaren utan appen, genom något form av API (gärna webbaserat och REST)
- Blockera strömbrytaren från att ansluta till Internet, men fortfarande fungera på det lokala nätverket.
(1) är framförallt för att jag vill kunna göra vad jag vill rent hemautomatiseringsmässigt, och (2) är framförallt av säkerhetsskäl då internetuppkopplade IoT-enheter sällan har särskilt bra säkerhet.
Existerande projekt⌗
Det finns en hel del intressanta tidigare projekt inom området, dessa är de jag kikat på och fått inspiration från.
- TinyTuya: Ett Python-bibliotek för att kommunicera med Tuya-baserade enheter.
- rust-tuyapi: Ett Rust-bibliotek för att kommunicera med Tuya-baserade enheter.
- rust-async-tuyapi: Ett Rust-bibliotek (
async
) för att kommunicera med Tuya-baserade enheter, baserat pårust-tuyapi
ovan.
Hitta strömbrytarens ID och nyckel⌗
Första steget i att ta kontroll över sin strömbrytare är att hitta brytarens ID och hemliga nyckel. Dessa behövs för att kunna kommunicera med brytaren direkt på samma LAN.
Det finns flera sätt att hitta ID och nyckel som behövs. Du behöver bara använda ett sätt.
Hur jag gjorde⌗
Eftersom jag redan hade en gammal oanvänd rootad Androidtelefon hemma, inspirerades jag av denna guiden, och gjorde såhär:
- Installerade en gammal version av appen, nämligen 3.6.1. Nej, det fungerar inte med nyaste versionen. Jag använde
adb install xxxxxx.apk
. - Loggade in med mitt konto
- Parkopplade min strömbrytare
- Hämtade hem filen
/data/data/com.tuya.smartlife/shared_prefs/preference_global_keyeuXXXXXXXX.xml
till min dator och namngav dencodes.xml
. (Jag användeadb shell
ochcat
för att få filens innehåll) - Körde skriptet
transform.py
från guiden ovan. id
ochkey
är de delar du behöver för att kunna styra strömbrytarna själv.
Andra guider⌗
- Följ instruktionerna från TinyTuya. Kortfattat: registrera dig som Tuya-utvecklare hämta hem koderna från deras utvecklarportal.
- Följ instruktionerna från tuya_code_extract. Kortfattat: installera Smart life-appen i en Android-emulator, logga in med ditt konto, och hämta nycklarna.
REST API⌗
Eftersom jag ville kunna styra strömbrytaren flexibelt byggde jag ett litet REST API, skrivet i Rust med axum, kring rust-async-tuyapi.
API:t stödjer flera strömbrytare (numreras från 0 och uppåt), och startas med hjälp av ID, nyckel, och IP-adress för brytaren.
Källkoden till projektet finns här: https://github.com/zozs/tuya-web.
Kortfattat så exponerar den dessa endpoints:
POST /outlet/:outlet_id
: Växlar uttaget mellan av och påPUT /outlet/:outlet_id/true
: Slår på strömmenPUT /outlet/:outlet_id/false
: Slår av strömmenGET /outlet/:outlet_id
: Returnerar nuvarande tillstånd på uttaget
Detta gör att jag dels kan koppla POST
-rutten till en vanlig knapp, i mitt fall en Flic-knapp. Samtidigt kan jag ha cronjobs som slår på och av ett uttag vid vissa tidpunkter. I mitt fall styr jag en växtlampa med följande Kubernetes CronJob.
apiVersion: batch/v1
kind: CronJob
metadata:
name: tuya-growing-light-on
spec:
schedule: "0 9 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: curl
image: quay.io/curl/curl
command: ["curl", "-v", "-X", "PUT", "http://tuya-web.default.svc.cluster.local/outlet/0/true"]
restartPolicy: OnFailure
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: tuya-growing-light-off
spec:
schedule: "0 17 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: curl
image: quay.io/curl/curl
command: ["curl", "-v", "-X", "PUT", "http://tuya-web.default.svc.cluster.local/outlet/0/false"]
restartPolicy: OnFailure
Undersök och blockera internettillgång⌗
När allt är klart blockerade jag internettillgången för strömbrytarens IP-adress i min router. I mitt fall med en brandväggsregel för PF.
#/etc/pf.conf
# Block smart plug from accessing internet
block in quick on guestnet from { 10.0.5.50 } to !(guestnet:network)
Samtidigt passade jag på att logga de DNS förfrågningar som gjordes i unbound
.
#/etc/unbound.conf
log-queries: yes
verbosity: 2
Loggarna kommer nedanför, men sammanfattningsvis:
- Det skickas en massa broadcasts på port 6667/UDP. Det verkar vara kopplat till auto-discovery för att appen ska kunna hitta strömbrytaren på samma Wi-Fi. Verkar inte göra något att dessa blockeras.
- Den gör en ny DHCP förfrågan ungefär var femte minut. Jag misstänker att det är för att den inte kan ansluta till molntjänsten? Oavsett vilket verkar det fungera ändå.
- Den gör DNS förfrågningar till
m2.tuyaeu.com
, och sen försöker den ansluta dit. Gissningsvis det som är den faktiska molntjänsten för att styra den med appen.
Loggar⌗
Mar 28 10:36:51 zone dhcpd[24647]: DHCPOFFER on 10.0.5.50 to 1c:90:ff:0a:72:88 via vlan2
Mar 28 10:36:51 zone dhcpd[24647]: DHCPREQUEST for 10.0.5.50 from 1c:90:ff:0a:72:88 via vlan2
Mar 28 10:36:51 zone dhcpd[24647]: DHCPACK on 10.0.5.50 to 1c:90:ff:0a:72:88 via vlan2
Mar 28 10:37:03 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:08 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:13 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:18 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:23 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:28 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:33 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:38 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:43 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:44 zone unbound: [85358:0] info: 10.0.5.50 m2.tuyaeu.com. A IN
Mar 28 10:37:44 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:37:46 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:37:48 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:37:48 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:50 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:37:52 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:37:53 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:37:54 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:37:56 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:37:58 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:37:58 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172
Mar 28 10:38:00 rule 0/(match) block in on vlan2: 10.0.5.50.40188 > 35.156.42.116.8886: S 357465146:357465146(0) win 4380 <mss 1460>
Mar 28 10:38:03 rule 0/(match) block in on vlan2: 10.0.5.50.59727 > 255.255.255.255.6667: udp 172