zwave.scm 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. ;;; Guile-ZWave -- Guile talks with ZWave devices.
  2. ;;; Copyright © 2019, 2021 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of Guile-ZWave.
  5. ;;;
  6. ;;; Guile-ZWave is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; Guile-ZWave is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with Guile-ZWave. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (zwave)
  19. #:use-module (rnrs bytevectors)
  20. #:use-module (ice-9 binary-ports)
  21. #:use-module (ice-9 iconv)
  22. #:use-module (ice-9 vlist)
  23. #:use-module (ice-9 match)
  24. #:use-module (srfi srfi-1)
  25. #:use-module (srfi srfi-9)
  26. #:use-module (srfi srfi-9 gnu)
  27. #:use-module (srfi srfi-60)
  28. #:use-module (srfi srfi-71)
  29. #:export (open-zwave-serial-port
  30. initialize-zwave-state
  31. message-type
  32. message-class
  33. command-class
  34. serial-message?
  35. serial-message
  36. serial-message-type
  37. serial-message-class
  38. serial-message-payload
  39. read-serial-message
  40. write-serial-message
  41. make-request
  42. handle-response
  43. zwave-state?
  44. zwave-state-nodes
  45. zwave-state-version
  46. pop-zwave-state-notification))
  47. (define-immutable-record-type <zwave-state>
  48. (zwave-state version suc-node-id nodes notifications)
  49. zwave-state?
  50. (version zwave-state-version set-zwave-state-version)
  51. (suc-node-id zwave-state-suc-node-id set-zwave-state-suc-node-id)
  52. (nodes zwave-state-nodes set-zwave-state-nodes)
  53. (notifications zwave-state-notifications set-zwave-state-notifications))
  54. (define (push-zwave-state-notification state notification)
  55. (set-zwave-state-notifications state
  56. (cons notification
  57. (zwave-state-notifications state))))
  58. (define (pop-zwave-state-notification state)
  59. (match (zwave-state-notifications state)
  60. (()
  61. (values #f state))
  62. ((first ... notification)
  63. (values notification
  64. (set-zwave-state-notifications state first)))))
  65. (define %zwave-initial-state
  66. (zwave-state #f #f '() '()))
  67. (define-immutable-record-type <node>
  68. (%node id alive? command-classes)
  69. node?
  70. (id node-id)
  71. (alive? node-alive? set-node-alive?)
  72. (command-classes node-command-classes set-node-command-classes))
  73. (define* (node id #:key (alive? #t) command-classes)
  74. (%node id alive? command-classes))
  75. (define (add-zwave-state-node state node)
  76. "Add NODE to the list of nodes of STATE."
  77. (let ((new-id (node-id node)))
  78. (set-zwave-state-nodes
  79. state
  80. (cons node (remove (lambda (node)
  81. (= new-id (node-id node)))
  82. (zwave-state-nodes state))))))
  83. (define-syntax define-enumerate-type
  84. (syntax-rules ()
  85. ((_ record-type predicate
  86. name->value value->integer integer->value
  87. (name id) ...)
  88. (begin
  89. ;; Disjoint type used as a box for the integer value.
  90. (define-record-type record-type
  91. (box value)
  92. predicate
  93. (value value->integer))
  94. (define integer->value
  95. (let ((id/name-list '((id . name) ...)))
  96. (lambda (integer)
  97. (let ((symbol (assv-ref id/name-list integer)))
  98. (unless symbol
  99. (error "wrong enumerate value"
  100. (list 'record-type integer)))
  101. (box integer)))))
  102. (let* ((alist '((id . name) ...))
  103. (print-record (lambda (box port)
  104. (format port "#<~a ~a>"
  105. 'record-type
  106. (assv-ref alist
  107. (value->integer box))))))
  108. (set-record-type-printer! record-type print-record))
  109. (define-syntax name->value
  110. (syntax-rules (name ...)
  111. ((_ name) (make-struct/no-tail record-type id))
  112. ...))))))
  113. (define-enumerate-type <message-type>
  114. message-type?
  115. message-type message-type->integer integer->message-type
  116. (request #x00)
  117. (response #x01)
  118. (ack #x02)
  119. (nack #x03)
  120. (can #x04))
  121. ;; Signaling frames. This is a single byte sent over the wire.
  122. (define-enumerate-type <signaling-frame>
  123. signaling-frame?
  124. signaling-frame signaling-frame->integer integer->signaling-frame
  125. (som #x01) ;start of frame (SOM)
  126. (ack #x06)
  127. (nack #x15)
  128. (can #x18))
  129. ;; Message classes. See SerialMessage.java.
  130. (define-enumerate-type <message-class>
  131. message-class?
  132. message-class message-class->integer integer->message-class
  133. (get-initial-data #x02)
  134. (application-command-handler #x04)
  135. (get-version #x15)
  136. (application-update #x49)
  137. (get-suc-node-id #x56) ;get ID of Static Update Controller (SUC)
  138. (explore-request-inclusion #x5e)
  139. (get-routing-info #x80)
  140. (add-node-to-network #x4a)) ;start the device inclusion process
  141. (define-record-type <serial-message>
  142. (serial-message type class payload)
  143. serial-message?
  144. (type serial-message-type)
  145. (class serial-message-class)
  146. (payload serial-message-payload))
  147. (define-syntax vhash
  148. (syntax-rules (=>)
  149. "Build a vhash with the given key/value mappings."
  150. ((_ constructor)
  151. vlist-null)
  152. ((_ constructor (key => value) rest ...)
  153. (vhash-cons (constructor key) value
  154. (vhash constructor rest ...)))))
  155. (define %request-payload
  156. ;; Map message classes to procedures that compute the message's payload
  157. ;; based on its arguments. See ZWaveController.java.
  158. (vhash message-class
  159. (get-routing-info
  160. => (lambda (node-id)
  161. (u8-list->bytevector
  162. (list node-id
  163. 0 ;don't remove bad nodes
  164. 0 ;don't remove non-repeaters
  165. 3))))
  166. (add-node-to-network
  167. => (lambda* (#:key high-power? network-wide?)
  168. ;; Send a request to start the device "inclusion" process.
  169. ;; When HIGH-POWER? is false, the device has to be within one
  170. ;; meter of the controller. When NETWORK-WIDE? is true, hmm?
  171. ;; See AddNodeMessageClass.java.
  172. (define add-node-any 1)
  173. (define option-high-power #x80)
  174. (define option-network-wide #x40)
  175. (u8-list->bytevector
  176. (list (logior add-node-any
  177. (if high-power? option-high-power 0)
  178. (if network-wide? option-network-wide 0))))))))
  179. ;; Update types for 'application-update' messages.
  180. (define-enumerate-type <update-type>
  181. update-type?
  182. update-type update-type->integer integer->update-type
  183. (node-info-received #x84)
  184. (node-info-request-done #x82)
  185. (node-info-request-failed #x81)
  186. (routing-pending #x80)
  187. (new-id-assigned #x40)
  188. (delete-done #x20)
  189. (suc-id #x10))
  190. ;; Command classes that devices may support. See ZWaveCommandClass.java.
  191. (define-enumerate-type <command-class>
  192. command-class?
  193. command-class command-class->integer integer->command-class
  194. (no-operation #x00)
  195. (basic #x20)
  196. (controller-replication #x21)
  197. (application-status #x22)
  198. (zip #x23)
  199. (security-panel-mode #x24)
  200. (switch-binary #x25)
  201. (switch-multilevel #x26)
  202. (switch-all #x27)
  203. (switch-toggle-binary #x28)
  204. (switch-toggle-multilevel #x29)
  205. (chimney-fan #x2a)
  206. (scene-activation #x2b)
  207. (scene-actuator-conf #x2c)
  208. (scene-controller-conf #x2d)
  209. (security-panel-zone #x2e)
  210. (security-panel-zone-sensor #x2f)
  211. (sensor-binary #x30)
  212. (sensor-multilevel #x31)
  213. (meter #x32)
  214. (switch-color #x33)
  215. (network-management-inclusion #x34)
  216. (meter-pulse #x35)
  217. (basic-tariff-info #x36)
  218. (hrv-status #x37)
  219. (hrv-control #x39)
  220. (dcp-config #x3a)
  221. (dcp-monitor #x3b)
  222. (meter-tbl-config #x3c)
  223. (meter-tbl-monitor #x3d)
  224. (meter-tbl-push #x3e)
  225. (thermostat-heating #x38)
  226. (prepayment #x3f)
  227. (thermostat-mode #x40)
  228. (prepayment-encapsulation #x41)
  229. (thermostat-operating-state #x42)
  230. (thermostat-setpoint #x43)
  231. (thermostat-fan-mode #x44)
  232. (thermostat-fan-state #x45)
  233. (climate-control-schedule #x46)
  234. (thermostat-setback #x47)
  235. (rate-tbl-config #x48)
  236. (rate-tbl-monitor #x49)
  237. (tariff-config #x4a)
  238. (tariff-tbl-monitor #x4b)
  239. (door-lock-logging #x4c)
  240. (network-management-basic #x4d)
  241. (schedule-entry-lock #x4e)
  242. (zip-6lowpan #x4f)
  243. (basic-window-covering #x50)
  244. (mtp-window-covering #x51)
  245. (network-management-proxy #x52)
  246. (schedule #x53)
  247. (network-management-primary #x54)
  248. (transport-service #x55)
  249. (crc-16-encap #x56)
  250. (application-capability #x57)
  251. (zip-nd #x58)
  252. (association-grp-info #x59)
  253. (device-reset-locally #x5a)
  254. (central-scene #x5b)
  255. (ip-association #x5c)
  256. (antitheft #x5d)
  257. (zwaveplus-info #x5e)
  258. (zip-gateway #x5f)
  259. (multi-channel #x60)
  260. (zip-portal #x61)
  261. (door-lock #x62)
  262. (user-code #x63)
  263. (humidity-control-setpoint #x64)
  264. (dmx #x65)
  265. (barrier-operator #x66)
  266. (network-management-installation-maintenance #x67)
  267. (zip-naming #x68)
  268. (mailbox #x69)
  269. (window-covering #x6a)
  270. (irrigation #x6b)
  271. (supervision #x6c)
  272. (humidity-control-mode #x6d)
  273. (humidity-control-operating-state #x6e)
  274. (entry-control #x6f)
  275. (configuration #x70)
  276. (alarm #x71)
  277. (manufacturer-specific #x72)
  278. (powerlevel #x73)
  279. (inclusion-controller #x74)
  280. (protection #x75)
  281. (lock #x76)
  282. (node-naming #x77)
  283. (firmware-update-md #x7a)
  284. (grouping-name #x7b)
  285. (remote-association-activate #x7c)
  286. (remote-association #x7d)
  287. (battery #x80)
  288. (clock #x81)
  289. (hail #x82)
  290. (wake-up #x84)
  291. (association #x85)
  292. (version #x86)
  293. (indicator #x87)
  294. (proprietary #x88)
  295. (language #x89)
  296. (time #x8a)
  297. (time-parameters #x8b)
  298. (geographic-location #x8c)
  299. (multi-channel-association #x8e)
  300. (multi-cmd #x8f)
  301. (energy-production #x90)
  302. ;; Note that MANUFACTURER-PROPRIETARY shouldn't be instantiated directly
  303. ;; The getInstance method will catch this and translate to the correct
  304. ;; class for the device.
  305. (manufacturer-proprietary #x91)
  306. (screen-md #x92)
  307. (screen-attributes #x93)
  308. (simple-av-control #x94)
  309. (av-content-directory-md #x95)
  310. (av-renderer-status #x96)
  311. (av-content-search-md #x97)
  312. (security #x98)
  313. (av-tagging-md #x99)
  314. (ip-configuration #x9a)
  315. (association-command-configuration #x9b)
  316. (sensor-alarm #x9c)
  317. (silence-alarm #x9d)
  318. (sensor-configuration #x9e)
  319. (security-2 #x9f)
  320. (mark #xef)
  321. (non-interoperable #xf0))
  322. (define (lookup-node state id)
  323. "Return the node with the given ID or #f."
  324. (find (lambda (node)
  325. (= id (node-id node)))
  326. (zwave-state-nodes state)))
  327. (define (handle-get-initial-data message state)
  328. "Process MESSAGE, a response to a 'get-initial-data' request, and return
  329. STATE with a node list adjusted accordingly."
  330. (let* ((payload (serial-message-payload message))
  331. (bytes (bytevector-u8-ref payload 2)))
  332. (unless (= 29 bytes)
  333. (error "invalid get-initial-data number of node bytes"
  334. bytes))
  335. (let loop ((index 3)
  336. (id 1)
  337. (nodes '()))
  338. (if (< index (+ 3 bytes)) ;XXX: or +2?
  339. (let ((byte (bytevector-u8-ref payload index)))
  340. ;; Each bit in BYTE indicates a node.
  341. (let liip ((bit 0)
  342. (id id)
  343. (nodes nodes))
  344. (if (< bit 8)
  345. (liip (+ 1 bit)
  346. (+ 1 id)
  347. (if (bit-set? bit byte)
  348. (cons (node id) nodes)
  349. nodes))
  350. (loop (+ 1 index) id nodes))))
  351. (set-zwave-state-nodes state (reverse nodes))))))
  352. (define (handle-application-update message state)
  353. "Handle MESSAGE, an 'application-update' message, and return STATE modified
  354. accordingly."
  355. (define (read-command-classes payload)
  356. (define len (bytevector-u8-ref payload 2))
  357. (let loop ((index 6)
  358. (classes '()))
  359. (if (< index (+ len 2))
  360. (loop (+ 1 index)
  361. (cons (integer->command-class
  362. (bytevector-u8-ref payload index))
  363. classes))
  364. (reverse classes))))
  365. (let* ((payload (serial-message-payload message))
  366. (update (integer->update-type
  367. (bytevector-u8-ref payload 0))))
  368. (cond ((equal? update (update-type node-info-received))
  369. ;; We received a "node info frame" (NIF).
  370. (let* ((id (bytevector-u8-ref payload 1))
  371. (node (or (lookup-node state id)
  372. (node id))))
  373. (pk 'newnode id)
  374. (add-zwave-state-node state
  375. (set-node-command-classes
  376. node (read-command-classes payload)))))
  377. (else
  378. state))))
  379. (define (sub-bytevector bv start size)
  380. "Return a copy of the SIZE bytes of BV starting from offset START."
  381. (let ((result (make-bytevector size)))
  382. (bytevector-copy! bv start result 0 size)
  383. result))
  384. (define (handle-get-version message state)
  385. "Handle MESSAGE, a 'get-version' reply that gives us the version string of
  386. the controller."
  387. (let* ((payload (serial-message-payload message))
  388. (library-type (bytevector-u8-ref payload 11))
  389. (version (utf8->string (sub-bytevector payload 0 11))))
  390. (set-zwave-state-version state version)))
  391. (define (handle-application-command-handler message state)
  392. (let* ((payload (serial-message-payload message))
  393. (node-id (bytevector-u8-ref payload 1))
  394. (len (bytevector-u8-ref payload 2))
  395. (class (integer->command-class
  396. (bytevector-u8-ref payload 3))))
  397. (push-zwave-state-notification state
  398. (cons class node-id))))
  399. (define (handle-add-node-to-network message state)
  400. "Handle the 'add-node-to-network' request in MESSAGE."
  401. ;; See AddNodeMessageClass.java.
  402. (define status/learn-ready 1)
  403. (define status/node-found 2)
  404. (define status/adding-slave 3) ;XXX: despicable terminology
  405. (define status/adding-controller 4)
  406. (define status/protocol-done 5)
  407. (define status/done 6)
  408. (define status/failed 7)
  409. (define (payload->command-classes payload)
  410. ;; TODO: Factorize with 'read-command-classes'?
  411. (let ((basic (bytevector-u8-ref payload 4))
  412. (generic (bytevector-u8-ref payload 5))
  413. (specific (bytevector-u8-ref payload 6)))
  414. (let loop ((i 7)
  415. (classes '()))
  416. (if (= i (bytevector-length payload))
  417. (reverse classes)
  418. (let ((integer (bytevector-u8-ref payload i)))
  419. (match (false-if-exception (integer->command-class integer))
  420. (#f (loop (+ 1 i) classes))
  421. (class (loop (+ 1 i) (cons class classes)))))))))
  422. ;; TODO: Implement.
  423. (let* ((payload (serial-message-payload message))
  424. (status (bytevector-u8-ref payload 1)))
  425. (cond ((= status status/learn-ready)
  426. (pk 'learn-ready)
  427. state)
  428. ((= status status/node-found)
  429. (pk 'node-found!)
  430. state)
  431. ((= status status/adding-slave)
  432. (let* ((id (bytevector-u8-ref payload 2))
  433. (classes (payload->command-classes payload))
  434. (node (node id #:command-classes classes)))
  435. (add-zwave-state-node
  436. (push-zwave-state-notification state
  437. `(included-node . ,node))
  438. (pk 'adding-slave node))))
  439. ((= status status/adding-controller)
  440. (pk 'adding-controller (bytevector-u8-ref payload 2))
  441. (let ((id (bytevector-u8-ref payload 2))
  442. (classes (payload->command-classes payload)))
  443. (add-zwave-state-node
  444. (push-zwave-state-notification state
  445. `(included-controller . ,node))
  446. (pk 'adding-controller
  447. (node id #:command-classes classes)))))
  448. ((= status status/protocol-done)
  449. (let* ((id (bytevector-u8-ref payload 2))
  450. (node (lookup-node state id)))
  451. (pk 'protocol-done id node)
  452. (push-zwave-state-notification state
  453. `(inclusion-protocol-done . ,node))))
  454. ((= status status/done)
  455. (pk 'done (bytevector-u8-ref payload 2))
  456. state)
  457. ((= status status/failed)
  458. (pk 'failed)
  459. (push-zwave-state-notification state 'inclusion-failed))
  460. (else
  461. (format (current-error-port) "unknown add-node status: ~s~%"
  462. status)
  463. state))))
  464. (define %response-handlers
  465. (vhash message-class
  466. (application-update => handle-application-update)
  467. (get-initial-data => handle-get-initial-data)
  468. (get-version => handle-get-version)
  469. (application-command-handler => handle-application-command-handler)
  470. (add-node-to-network => handle-add-node-to-network)))
  471. (define (handle-response response state)
  472. "Return STATE updated according to RESPONSE."
  473. (let ((type (serial-message-type response)))
  474. (cond ((equal? (message-type ack) type)
  475. state)
  476. ((equal? (message-type nack) type)
  477. state)
  478. ((equal? (message-type can) type)
  479. state)
  480. (else
  481. (match (vhash-assoc (serial-message-class response)
  482. %response-handlers)
  483. (#f
  484. (format (current-error-port)
  485. "warning: no handler for response ~s~%"
  486. response)
  487. state)
  488. ((_ . handle) (handle response state)))))))
  489. (define (make-request message-class . args)
  490. "Return a message for a request of MESSAGE-CLASS with the given ARGS.
  491. Some requests take no arguments while others require some arguments."
  492. (serial-message (message-type request)
  493. message-class
  494. (match (vhash-assoc message-class %request-payload)
  495. (#f #vu8())
  496. ((_ . payload) (apply payload args)))))
  497. (define* (bytevector-checksum bv #:optional (start 0))
  498. "Compute the checksum of BV, a bytevector, starting at offset START."
  499. (let loop ((i start)
  500. (sum #xff))
  501. (if (= i (bytevector-length bv))
  502. sum
  503. (loop (+ i 1)
  504. (logxor sum (bytevector-u8-ref bv i))))))
  505. ;; Quoth, openHAB: A ZWave serial message frame is made up as follows
  506. ;; Byte 0 : SOF (Start of Frame) 0x01
  507. ;; Byte 1 : Length of frame - number of bytes to follow
  508. ;; Byte 2 : Request (0x00) or Response (0x01)
  509. ;; Byte 3 : Message Class (see SerialMessageClass)
  510. ;; Byte 4+: Message Class data >> Message Payload
  511. ;; Byte x : Last byte is checksum
  512. (define (write-serial-message message port)
  513. "Write MESSAGE to PORT."
  514. (cond ((equal? (serial-message-type message) (message-type nack))
  515. (put-u8 port (signaling-frame nack)))
  516. ((equal? (serial-message-type message) (message-type ack))
  517. (put-u8 port (signaling-frame ack)))
  518. ((equal? (serial-message-type message) (message-type can))
  519. (put-u8 port (signaling-frame can)))
  520. (else
  521. (let* ((len (+ (bytevector-length (serial-message-payload message))
  522. (if (equal? (serial-message-type message)
  523. (message-type request))
  524. 5
  525. 4)))
  526. (bv (make-bytevector len)))
  527. (bytevector-u8-set! bv 0
  528. (signaling-frame->integer
  529. (signaling-frame som)))
  530. (bytevector-u8-set! bv 1 (- len 2))
  531. (bytevector-u8-set! bv 2
  532. (message-type->integer
  533. (serial-message-type message)))
  534. (bytevector-u8-set! bv 3
  535. (message-class->integer
  536. (serial-message-class message)))
  537. (bytevector-copy! (serial-message-payload message) 0
  538. bv 4
  539. (bytevector-length
  540. (serial-message-payload message)))
  541. ;; TODO: request callback id
  542. (bytevector-u8-set! bv (- len 1) 1)
  543. (bytevector-u8-set! bv (- len 1) (bytevector-checksum bv))
  544. (put-bytevector port bv)))))
  545. (define (read-serial-message port)
  546. (let ((type (integer->signaling-frame (get-u8 port))))
  547. (cond ((equal? (signaling-frame ack) type)
  548. (serial-message (message-type ack) 0 #vu8()))
  549. ((equal? (signaling-frame nack) type)
  550. (serial-message (message-type nack) 0 #vu8()))
  551. ((equal? (signaling-frame can) type)
  552. (serial-message (message-type can) 0 #vu8()))
  553. ((equal? (signaling-frame som) type)
  554. (let* ((len (get-u8 port))
  555. (bv (make-bytevector (+ len 2))))
  556. (bytevector-u8-set! bv 0 (signaling-frame->integer type))
  557. (bytevector-u8-set! bv 1 len)
  558. (get-bytevector-n! port bv 2 len)
  559. (let ((type (if (zero? (bytevector-u8-ref bv 2))
  560. (message-type request)
  561. (message-type response)))
  562. (class (integer->message-class
  563. (bytevector-u8-ref bv 3)))
  564. (payload (make-bytevector (- len 4)))
  565. (sum (bytevector-u8-ref bv (- len 1))))
  566. (bytevector-copy! bv 4 payload 0 (- len 4))
  567. (bytevector-u8-set! bv (- len 1) 1)
  568. (unless (= sum (bytevector-checksum bv))
  569. (error "invalid checksum"
  570. (list sum (bytevector-checksum bv))))
  571. ;; Send an ACK. If we don't do that, the controller will
  572. ;; resend the message three times.
  573. (put-u8 port (signaling-frame->integer
  574. (signaling-frame ack)))
  575. (serial-message type class payload))))
  576. (else
  577. (error "unknown message frame" type)))))
  578. ;; (define (make-command node-id command-class . args)
  579. ;; ;; TODO: Return a message for a command like BatteryGet.
  580. ;; (serial-message (message-type request)
  581. ;; (message-class application-command-handler)
  582. ;; (u8-list->bytevector
  583. ;; (cons* 0 node-id
  584. ;; (command-class->integer command-class)
  585. ;; args))))
  586. (define (initialize-zwave-state port)
  587. "Initialize ZWave state by sending a messages on PORT, and return the
  588. state."
  589. (write-serial-message (make-request (message-class get-version))
  590. port)
  591. (let loop ()
  592. (let* ((message (read-serial-message port))
  593. (state (handle-response message %zwave-initial-state)))
  594. (if (eq? state %zwave-initial-state) ;did we get a mere 'ack' or similar?
  595. (loop)
  596. state))))
  597. (define (open-zwave-serial-port file)
  598. (let ((port (open-file file "r+0")))
  599. ;; TODO: Open at 115200 baud 8N1.
  600. (put-u8 port (signaling-frame->integer (signaling-frame nack)))
  601. port))