Skip to content

Instantly share code, notes, and snippets.

@olimsaidov
Created January 14, 2021 15:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save olimsaidov/41f79b2e203270574af85e74323b3be1 to your computer and use it in GitHub Desktop.
Save olimsaidov/41f79b2e203270574af85e74323b3be1 to your computer and use it in GitHub Desktop.
Clojure YeeLight Controller
#!/usr/bin/env bb
(ns app.yeelight
(:import [java.net DatagramPacket DatagramSocket
InetAddress URI Socket])
(:require [clojure.string :as str]
[clojure.walk :as walk]
[clojure.java.io :as io]
[cheshire.core :as json]
[clojure.tools.cli :as cli]))
(defn parse-device
[device]
(let [lines (->> (str/split device #"\r\n")
(remove empty?))]
(->> (mapv (fn [line] (let [match (re-find #"(.*): (.*)" line)]
(when (= 3 (count match))
[(str/lower-case (match 1))
(match 2)])))
lines)
(remove nil?)
(into {})
walk/keywordize-keys)))
(defn discover
[]
(let [send-data (.getBytes (str/join "\r\n" ["M-SEARCH * HTTP/1.1"
"Host: 239.255.255.250:1982"
"MAN: \"ssdp:discover\""
"ST: wifi_bulb"]))
receive-data (byte-array 1024)
send-packet (DatagramPacket. send-data (count send-data) (InetAddress/getByName "239.255.255.250") 1982)
client-socket (doto (DatagramSocket.) (.send send-packet))
receive-packet (DatagramPacket. receive-data (count receive-data))]
(parse-device (do (.receive client-socket receive-packet)
(new String (.getData receive-packet))))))
(defn call-method
[uri method params]
(with-open [socket (Socket. (.getHost (URI. uri)) (.getPort (URI. uri)))
writer (io/writer (.getOutputStream socket))
reader (io/reader (.getInputStream socket))]
(.write writer (str (json/encode {:id (rand-int 10000)
:method method
:params params})
"\r\n"))
(.flush writer)
(json/decode (.readLine reader) true)))
(defn rgb->int
[r g b]
(+ (* 65536 r) (* 256 g) b))
(def cli-options
[["-r" "--red BYTE" "Red color" :default (rand-int 256) :parse-fn #(Integer/parseInt %) :validate [#(<= 0 % 255) "Must be a number between 0 and 255"]]
["-g" "--green BYTE" "Green color" :default (rand-int 256) :parse-fn #(Integer/parseInt %) :validate [#(<= 0 % 255) "Must be a number between 0 and 255"]]
["-b" "--blue BYTE" "Blue color" :default (rand-int 256) :parse-fn #(Integer/parseInt %) :validate [#(<= 0 % 255) "Must be a number between 0 and 255"]]])
(let [options (:options (cli/parse-opts *command-line-args* cli-options))]
(call-method (:location (discover)) :set_rgb
[(rgb->int (:red options) (:green options) (:blue options)) "smooth" 400]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment