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
Uh oh!
There was an error while loading. Please reload this page.
-
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