My new apartment was too cold #151
Grazfather
started this conversation in
War stories
Replies: 2 comments 2 replies
-
This is awesome @Grazfather! Thanks for sharing! |
Beta Was this translation helpful? Give feedback.
1 reply
-
It's sloppy stuff, I wrote it because I wanted to learn clojure. (ns tempplot.core
(:require [dvlopt.linux.i2c :as i2c]
[dvlopt.linux.i2c.smbus :as smbus]
[clojure.data.json :as json]
[org.httpkit.client :as http]
[oz.core :as oz]
[clojure.java.jdbc :refer :all]
[honeysql.core :as sql]))
;; Util
(defmacro when-let*
([bindings & body]
(if (seq bindings)
`(when-let [~(first bindings) ~(second bindings)]
(when-let* ~(drop 2 bindings) ~@body))
`(do ~@body))))
(defn c->f
[tempc]
(+ (/ (* tempc 9) 5) 32))
;; DB
(def db
{:classname "org.sqlite.JDBC"
:subprotocol "sqlite"
:subname "db/database.db"})
(defn create-db
[db]
(try (db-do-commands
db (create-table-ddl :temps
[[:timestamp :datetime :default :current_timestamp ]
[:location :text]
[:temp :real]]
:conditional?))
(catch Exception e
(println (.getMessage e)))))
(def data-query {:select [:*]
:from [:temps]
:where [:> :timestamp "2021-03-01T00:00:00"]
:order-by [:timestamp]})
(defn get-data [db]
(query db (sql/format data-query)))
(defn minimum-legal-temp
[datetime outside-temp]
; TODO: Add date awareness: Heat season is between Oct 1 and ??
(if (and (>= (.getHour datetime) 6) (< (.getHour datetime) 22))
(if (< outside-temp 12.78) ; Day time, if under 55°F, then keep 68°F
20
outside-temp) ; Otherwise there's no miniimum
16.67)) ; At night the minimum is always 62°F during heat season
;; Temp sensor
(def reg-temp 0)
(def reg-config 1)
(def resolution 0.0078125)
(defn bytes->val
[b]
(reduce (fn [acc v] (+ v (* acc 256))) b))
(def reading->C (partial * resolution))
(defn read-reg
[bus reg]
(i2c/transaction bus
[{::i2c/slave-address 0x48
::i2c/write [reg]}
{::i2c/slave-address 0x48
::i2c/read 2}]))
;; Plot
(defn line-plot
[db]
{:data {:values (mapv #(assoc % :temp (c->f (:temp %))) (get-data db))}
:encoding {:x {:field "timestamp"
:type "temporal"
:axis {:title "Time and date"}}
:y {:field "temp"
:type "quantitative"
:scale {:type "linear" :domain [{:expr "lowTemp"} {:expr "hiTemp"}]}
:axis {:title "Temperature (°F)"}}
:color {:field "location" :type "nominal"}}
:params [
{:name "lowYear"
:value 2021
:bind {:input "range" :min 2020 :max 2022 :step 1}}
{:name "lowMonth"
:value 1
:bind {:input "range" :min 1 :max 12 :step 1}}
{:name "lowDay"
:value 1
:bind {:input "range" :min 1 :max 31 :step 1}}
#_{:name "hiTime"
:value "datetime(2021, 02, 20)"
:bind {:input "range" :min "datetime(2021, 02, 15)" :max "datetime(2021, 02, 22)" :step "datetime(0, 0, 1)"}}
{:name "lowTemp"
:value 20
:bind {:input "range" :min 1 :max 100 :step 1}}
{:name "hiTemp"
:value 80
:bind {:input "range" :min 1 :max 100 :step 1}}
]
:transform [
#_{:filter {:field "timestamp" :gte {:year {:expr "lowYear"}
:month {:expr "lowMonth"}
:date {:expr "lowDay"}}}}
{:filter {:field "timestamp" :gte {:year 2021
:month 02
:date 20}}}
{:filter {:and [{:field "temp" :gte {:expr "lowTemp"}}
{:field "temp" :lte {:expr "hiTemp"}}]}}
]
:width 600
:height 300
:mark "line"})
(defn get-dashboard
[db]
[:div
[:h3 "Apartment temperature vs outside temperature"
[:div [:vega-lite (line-plot db)]]]])
;; Render the plot
(defn start-plot-server [] (oz/start-server!))
(defn view [dashboard]
(oz/view! dashboard))
(comment
(start-plot-server)
(view (get-dashboard db)))
;; API
(def api-key "FILLME")
(def api-current-weather "https://api.openweathermap.org/data/2.5/weather?id=%d&appid=%s&mode=json&units=metric")
(def manhattan-city-id 5125771)
(defn get-current-temperature
[city-id]
(let [url (format api-current-weather city-id api-key)
resp @(http/get url)
err (:error resp)]
(when (= err nil)
(-> resp
:body
json/read-str
(get-in ["main" "temp"])
float))))
(defn get-manhattan-temp [] (get-current-temperature manhattan-city-id))
(defn -main
[& args]
(println "Creating db")
(create-db db)
(println "Reading temps")
(with-open [bus (i2c/bus "/dev/i2c-1")]
(while true
(when-let* [outside-temp (get-manhattan-temp)
inside-temp (-> (read-reg bus reg-temp)
(get 1)
bytes->val
reading->C)
now (java.time.LocalDateTime/now)
min-temp (minimum-legal-temp now outside-temp)]
(insert! db :temps {:location "inside" :temp inside-temp :timestamp now})
(insert! db :temps {:location "outside" :temp outside-temp :timestamp now})
(insert! db :temps {:location "minimum" :temp min-temp :timestamp now})
(println (format "%s inside %.2fC outside %.2fC illegal? %s" now inside-temp outside-temp (< inside-temp min-temp))))
(Thread/sleep (* 5 60 1000)) ; 5 minutes
)
)
(spit "plot.edn" (line-plot db))
) |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Maybe best summed up in this tweet:
https://twitter.com/Grazfather/status/1361688395806879749
My apartment was too cold, but its heating is controlled by the management. I looked into the laws around this for NYC and bought an i2c temperature sensor and connected it to my raspberry pi. I plot it against the temperature outside I get from some API, and finally against the legal minimum, which changes based on time, date, and outside temperature.
Was first saving it to a sqlite3 db and pulling the DB to another machine to display, but I am now working to export it as a static page which I can host from the raspberry pi itself.
Beta Was this translation helpful? Give feedback.
All reactions