Initial code checkin, untested
This commit is contained in:
parent
00861c6d6c
commit
5b62a20bba
|
@ -1,2 +1,210 @@
|
|||
(ns suanni.syno-eyes
|
||||
(:require [suanni.eyes :refer [SuanniEyes]]))
|
||||
(:require [suanni.eyes :refer [SuanniEyes]]
|
||||
[fudo-clojure.http.client :as client]
|
||||
[fudo-clojure.http.request :as req]
|
||||
[fudo-clojure.logging :as log]
|
||||
[fudo-clojure.result :as result]
|
||||
[clojure.string :as str])
|
||||
(:import java.net.InetAddress))
|
||||
|
||||
;; ### BaseSynoClient
|
||||
;;
|
||||
;; In order to fully initialize the SynoClient, we need to be able to query the
|
||||
;; Synology host to get path/version information and to authenticate. This base
|
||||
;; client will have enough functionality to do that. Calling `authenticate!` on
|
||||
;; the base client will actually perform the queries necessary to initialize
|
||||
;; full functionality.
|
||||
|
||||
(defprotocol IBaseSynoClient
|
||||
(get! [_ req])
|
||||
(initialize! [_ username passwd]))
|
||||
|
||||
;; ### SynoClient
|
||||
;;
|
||||
;; The SynoClient is the client that is actually able to do things like list
|
||||
;; cameras and take snapshots.
|
||||
|
||||
(defprotocol ISynoClient
|
||||
(disconnect! [_])
|
||||
(camera-snapshot! [_ camera-id])
|
||||
(list-cameras! [_])
|
||||
(get-camera-by-location! [_ loc]))
|
||||
|
||||
(defprotocol ICamera
|
||||
(id [_])
|
||||
(location [_])
|
||||
(vendor [_])
|
||||
(model [_])
|
||||
(host [_])
|
||||
(port [_])
|
||||
(take-snapshot! [_]))
|
||||
|
||||
(defrecord Camera [conn data]
|
||||
ICamera
|
||||
(id [_] (:id data))
|
||||
(location [_] (-> data :newName keyword))
|
||||
(vendor [_] (-> data :vendor))
|
||||
(model [_] (-> data :model))
|
||||
(host [_] (-> data :ip))
|
||||
(port [_] (-> data :port))
|
||||
(take-snapshot! [self] (camera-snapshot! conn (id self))))
|
||||
|
||||
(defn- perform-request! [http-client req]
|
||||
(result/bind (client/execute-request! http-client req)
|
||||
(fn [resp]
|
||||
(if (:error resp)
|
||||
(throw (ex-info "error performing request"
|
||||
{:request req
|
||||
:error (:error resp)
|
||||
:response resp}))
|
||||
(cond (:data resp) (:data resp)
|
||||
(:body resp) (:body resp))))))
|
||||
|
||||
(defn- get-hostname []
|
||||
(-> (InetAddress/getLocalHost)
|
||||
(.getHostName)))
|
||||
|
||||
(defn- make-api-info-request
|
||||
[{api :api}]
|
||||
(-> (req/base-request)
|
||||
(req/with-path "/webapi/query.cgi")
|
||||
(req/withQueryParams
|
||||
{
|
||||
:api :SYNO.API.Info
|
||||
:method :Query
|
||||
:version 1
|
||||
:query api
|
||||
})))
|
||||
|
||||
(defn- make-auth-request
|
||||
[{max-version :maxVersion
|
||||
path :path
|
||||
account :account
|
||||
passwd :passwd}]
|
||||
(-> (req/base-request)
|
||||
(req/with-path (format "/webapi/%s" path))
|
||||
(req/with-query-params
|
||||
{
|
||||
:version max-version
|
||||
:session :SurveillanceStation
|
||||
:api :SYNO.API.Auth
|
||||
:method :login
|
||||
:account account
|
||||
:passwd passwd
|
||||
:format :sid
|
||||
:enable_device_token true
|
||||
:device_name (get-hostname)
|
||||
})))
|
||||
|
||||
(defn- make-logout-request
|
||||
[{max-version :maxVersion path :path}]
|
||||
(-> (req/base-request)
|
||||
(req/with-path (format "/webapi/%s" path))
|
||||
(req/withQueryParams
|
||||
{
|
||||
:version max-version
|
||||
:session :SurveillanceStation
|
||||
:api :SYNO.API.Auth
|
||||
:method :Logout
|
||||
})))
|
||||
|
||||
(defn- make-list-cameras-request [{max-version :maxVersion path :path}]
|
||||
(-> (req/base-request)
|
||||
(req/with-path (format "webapi/%s" path))
|
||||
(req/with-query-params
|
||||
{
|
||||
:version max-version
|
||||
:session :SurveillanceStation
|
||||
:api :SYNO.SurveillanceStation.Camera
|
||||
:method :List
|
||||
})))
|
||||
|
||||
(defn- make-snapshot-request
|
||||
[{max-version :maxVersion path :path} camera-id]
|
||||
(-> (req/base-request)
|
||||
(req/with-path (format "/webapi/%s" path))
|
||||
(req/with-response-format :binary)
|
||||
(req/with-option :as :byte-array)
|
||||
(req/withQueryParams
|
||||
{
|
||||
:version max-version
|
||||
:session :SurveillanceStation
|
||||
:api :SYNO.SurveillanceStation.Camera
|
||||
:method :GetSnapshot
|
||||
:id camera-id
|
||||
})))
|
||||
|
||||
(defn- get-api-info! [conn api]
|
||||
(some-> conn
|
||||
(get! (make-api-info-request {:api api}))
|
||||
api))
|
||||
|
||||
(defn- authenticate! [conn username passwd]
|
||||
(->> (get-api-info! conn :SYNO.API.Auth)
|
||||
(merge {:username username :passwd passwd})
|
||||
(make-auth-request)
|
||||
(get! conn)))
|
||||
|
||||
(defn- find-first [pred lst]
|
||||
(loop [els lst]
|
||||
(if (pred (first els))
|
||||
(first els)
|
||||
(recur (rest els)))))
|
||||
|
||||
(defrecord SynoClient [conn auth-info api-info logger]
|
||||
IBaseSynoClient
|
||||
(get! [_ req]
|
||||
(get! conn
|
||||
(-> req
|
||||
(req/with-query-params
|
||||
{:device_id (:device_id auth-info)
|
||||
:_sid (:sid auth-info)}))))
|
||||
(initialize! [_ _ _]
|
||||
(throw (ex-info "client already initialized!" {})))
|
||||
|
||||
ISynoClient
|
||||
(disconnect! [self]
|
||||
(->> (:SYNO.API.Auth api-info)
|
||||
(make-logout-request)
|
||||
(get! self)))
|
||||
(camera-snapshot! [self camera-id]
|
||||
(log/info! logger (format "fetching snapshot from camera %s" camera-id))
|
||||
(get! self (make-snapshot-request (:SYNO.SurveillanceStation.Camera api-info) camera-id)))
|
||||
(list-cameras! [self]
|
||||
(log/info! logger "fetching camera list")
|
||||
(let [cams (into []
|
||||
(map (partial ->Camera self))
|
||||
(-> self
|
||||
(get! (make-list-cameras-request
|
||||
(:SYNO.SurveillanceStation.Camera api-info)))
|
||||
:cameras))]
|
||||
(log/info! logger (format "fetched %s cameras" (count cams)))
|
||||
cams))
|
||||
(get-camera-by-location! [self loc]
|
||||
(->> (list-cameras! self)
|
||||
(find-first (fn [cam] (= loc (location cam)))))))
|
||||
|
||||
(defn- initialize-connection!
|
||||
[conn & {username :username passwd :passwd logger :logger}]
|
||||
(let [api-info (into {} (map (fn [api] [api (get-api-info! conn api)]))
|
||||
[:SYNO.SurveillanceStation.Camera
|
||||
:SYNO.API.Auth])
|
||||
auth-info (authenticate! conn username passwd)]
|
||||
(->SynoClient conn auth-info api-info logger)))
|
||||
|
||||
(defn create [& {host :host port :port logger :logger}]
|
||||
(let [http-client (client/json-client)]
|
||||
(reify
|
||||
IBaseSynoClient
|
||||
(get! [_ req]
|
||||
(perform-request! http-client
|
||||
(-> req
|
||||
(req/with-host host)
|
||||
(req/with-port port)
|
||||
(req/with-option :insecure? true)
|
||||
(req/as-get))))
|
||||
(initialize! [self username passwd]
|
||||
(initialize-connection! self
|
||||
:username username
|
||||
:passwd passwd
|
||||
:logger logger)))))
|
||||
|
|
Loading…
Reference in New Issue