OCaml library for controlling Meross smart plugs via local HTTP API
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

json: rename mem -> member / finish -> seal across the codec + value API

Object combinators: [Object.mem] -> [Object.member], [Object.opt_mem]
-> [Object.opt_member], [Object.case_mem] -> [Object.case_member]. The
sibling submodules [Object.Mem] / [Object.Mems] become
[Object.Member] / [Object.Members]. RFC 8259 §4 calls these
"name/value pairs, referred to as the members", so mirror the spec
name rather than the shortened [mem].

[Object.finish] -> [Object.seal]. "Seal" reads as "close the map, no
more members added", which is what the operation does.

Value constructors/queries: [Value.mem] (function) -> [Value.member];
[Value.mem_find] -> [Value.member_key]; [Value.mem_names] ->
[Value.member_names]; [Value.mem_keys] -> [Value.member_keys].
[type mem = ...] -> [type member = ...]; [type object'] still points
at [member list].

Downstream (~80 files across slack, sbom, stripe, sigstore, requests,
claude, irmin, freebox) updated via perl-pie. dune build clean,
dune test ocaml-json clean.

+88 -88
+3 -3
dune-project
··· 24 24 (fmt (>= 0.9)) 25 25 (ipaddr (>= 5.0)) 26 26 (json (>= 0.1.0)) 27 - (bytesrw (>= 0.1.0)) 28 27 (logs (>= 0.7)) 29 28 (crypto-rng (>= 1.0)) 30 29 (ptime (>= 1.0)) 31 30 (uri (>= 4.0)) 32 31 (alcotest :with-test) 33 - (crowbar :with-test) 34 - (odoc :with-doc))) 32 + (alcobar :with-test) 33 + (odoc :with-doc) 34 + loc))
+2 -2
lib/abilities.ml
··· 16 16 let open Json.Codec in 17 17 Object.map ~kind:"ability_payload" (fun ability_map -> 18 18 { ability = String_map.fold (fun k _ acc -> k :: acc) ability_map [] }) 19 - |> Object.mem "ability" ability_map_codec ~enc:(fun _ -> String_map.empty) 20 - |> Object.skip_unknown |> Object.finish 19 + |> Object.member "ability" ability_map_codec ~enc:(fun _ -> String_map.empty) 20 + |> Object.skip_unknown |> Object.seal 21 21 22 22 (** {1 Operations} *) 23 23
+3 -3
lib/consumption.ml
··· 15 15 let open Json.Codec in 16 16 Object.map ~kind:"consumption_entry" (fun date value -> 17 17 { date; value = value /. 1000.0 }) (* Convert mWh to Wh *) 18 - |> Object.mem "date" string ~enc:(fun e -> e.date) 19 - |> Object.mem "value" number ~enc:(fun e -> e.value *. 1000.0) 20 - |> Object.skip_unknown |> Object.finish 18 + |> Object.member "date" string ~enc:(fun e -> e.date) 19 + |> Object.member "value" number ~enc:(fun e -> e.value *. 1000.0) 20 + |> Object.skip_unknown |> Object.seal 21 21 22 22 let codec = Json.Codec.list entry_codec 23 23
+24 -24
lib/device.ml
··· 28 28 mac_address = Option.value ~default:"" mac_address; 29 29 uuid = Option.value ~default:"" uuid; 30 30 }) 31 - |> Object.opt_mem "type" string ~enc:(fun h -> Some h.hw_type) 32 - |> Object.opt_mem "macAddress" string ~enc:(fun h -> Some h.mac_address) 33 - |> Object.opt_mem "uuid" string ~enc:(fun h -> Some h.uuid) 34 - |> Object.skip_unknown |> Object.finish 31 + |> Object.opt_member "type" string ~enc:(fun h -> Some h.hw_type) 32 + |> Object.opt_member "macAddress" string ~enc:(fun h -> Some h.mac_address) 33 + |> Object.opt_member "uuid" string ~enc:(fun h -> Some h.uuid) 34 + |> Object.skip_unknown |> Object.seal 35 35 36 36 type fw_info = { version : string } 37 37 ··· 39 39 let open Json.Codec in 40 40 Object.map ~kind:"firmware" (fun version -> 41 41 { version = Option.value ~default:"" version }) 42 - |> Object.opt_mem "version" string ~enc:(fun f -> Some f.version) 43 - |> Object.skip_unknown |> Object.finish 42 + |> Object.opt_member "version" string ~enc:(fun f -> Some f.version) 43 + |> Object.skip_unknown |> Object.seal 44 44 45 45 type toggle_info = { onoff : int } 46 46 47 47 let toggle_info_codec = 48 48 let open Json.Codec in 49 49 Object.map ~kind:"toggle" (fun onoff -> { onoff }) 50 - |> Object.mem "onoff" int ~enc:(fun t -> t.onoff) 51 - |> Object.skip_unknown |> Object.finish 50 + |> Object.member "onoff" int ~enc:(fun t -> t.onoff) 51 + |> Object.skip_unknown |> Object.seal 52 52 53 53 type system_info = { hardware : hw_info; firmware : fw_info } 54 54 55 55 let system_info_codec = 56 56 let open Json.Codec in 57 57 Object.map ~kind:"system" (fun hardware firmware -> { hardware; firmware }) 58 - |> Object.mem "hardware" hw_info_codec ~enc:(fun s -> s.hardware) 59 - |> Object.mem "firmware" fw_info_codec ~enc:(fun s -> s.firmware) 60 - |> Object.skip_unknown |> Object.finish 58 + |> Object.member "hardware" hw_info_codec ~enc:(fun s -> s.hardware) 59 + |> Object.member "firmware" fw_info_codec ~enc:(fun s -> s.firmware) 60 + |> Object.skip_unknown |> Object.seal 61 61 62 62 type digest_info = { togglex : toggle_info list } 63 63 ··· 65 65 let open Json.Codec in 66 66 Object.map ~kind:"digest" (fun togglex -> 67 67 { togglex = Option.value ~default:[] togglex }) 68 - |> Object.opt_mem "togglex" (list toggle_info_codec) ~enc:(fun d -> 68 + |> Object.opt_member "togglex" (list toggle_info_codec) ~enc:(fun d -> 69 69 Some d.togglex) 70 - |> Object.skip_unknown |> Object.finish 70 + |> Object.skip_unknown |> Object.seal 71 71 72 72 type all_info = { system : system_info; digest : digest_info option } 73 73 74 74 let all_info_codec = 75 75 let open Json.Codec in 76 76 Object.map ~kind:"all" (fun system digest -> { system; digest }) 77 - |> Object.mem "system" system_info_codec ~enc:(fun a -> a.system) 78 - |> Object.opt_mem "digest" digest_info_codec ~enc:(fun a -> a.digest) 79 - |> Object.skip_unknown |> Object.finish 77 + |> Object.member "system" system_info_codec ~enc:(fun a -> a.system) 78 + |> Object.opt_member "digest" digest_info_codec ~enc:(fun a -> a.digest) 79 + |> Object.skip_unknown |> Object.seal 80 80 81 81 type system_all_payload = { all : all_info } 82 82 83 83 let system_all_payload_codec = 84 84 let open Json.Codec in 85 85 Object.map ~kind:"system_all_payload" (fun all -> { all }) 86 - |> Object.mem "all" all_info_codec ~enc:(fun p -> p.all) 87 - |> Object.skip_unknown |> Object.finish 86 + |> Object.member "all" all_info_codec ~enc:(fun p -> p.all) 87 + |> Object.skip_unknown |> Object.seal 88 88 89 89 (** {1 Toggle Payload} *) 90 90 ··· 94 94 let open Json.Codec in 95 95 Object.map ~kind:"togglex" (fun channel onoff lm_time -> 96 96 { channel; onoff; lm_time }) 97 - |> Object.mem "channel" int ~enc:(fun t -> t.channel) 98 - |> Object.mem "onoff" int ~enc:(fun t -> t.onoff) 99 - |> Object.mem "lmTime" int ~enc:(fun t -> t.lm_time) 100 - |> Object.finish 97 + |> Object.member "channel" int ~enc:(fun t -> t.channel) 98 + |> Object.member "onoff" int ~enc:(fun t -> t.onoff) 99 + |> Object.member "lmTime" int ~enc:(fun t -> t.lm_time) 100 + |> Object.seal 101 101 102 102 type togglex_payload = { togglex : togglex_req } 103 103 104 104 let togglex_payload_codec = 105 105 let open Json.Codec in 106 106 Object.map ~kind:"togglex_payload" (fun togglex -> { togglex }) 107 - |> Object.mem "togglex" togglex_req_codec ~enc:(fun p -> p.togglex) 108 - |> Object.finish 107 + |> Object.member "togglex" togglex_req_codec ~enc:(fun p -> p.togglex) 108 + |> Object.seal 109 109 110 110 (** {1 Response Parsing} *) 111 111
+2 -2
lib/dnd.ml
··· 12 12 13 13 let codec = 14 14 Json.Codec.Object.map ~kind:"dnd_mode" (fun mode -> { enabled = mode = 1 }) 15 - |> Json.Codec.Object.mem "mode" Json.Codec.int ~enc:(fun d -> 15 + |> Json.Codec.Object.member "mode" Json.Codec.int ~enc:(fun d -> 16 16 if d.enabled then 1 else 0) 17 - |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.finish 17 + |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal 18 18 19 19 (** {1 Operations} *) 20 20
+4 -4
lib/electricity.ml
··· 21 21 voltage = voltage /. 10.0; 22 22 current = current /. 1000.0; 23 23 }) 24 - |> Object.mem "power" number ~enc:(fun e -> e.power *. 1000.0) 25 - |> Object.mem "voltage" number ~enc:(fun e -> e.voltage *. 10.0) 26 - |> Object.mem "current" number ~enc:(fun e -> e.current *. 1000.0) 27 - |> Object.skip_unknown |> Object.finish 24 + |> Object.member "power" number ~enc:(fun e -> e.power *. 1000.0) 25 + |> Object.member "voltage" number ~enc:(fun e -> e.voltage *. 10.0) 26 + |> Object.member "current" number ~enc:(fun e -> e.current *. 1000.0) 27 + |> Object.skip_unknown |> Object.seal 28 28 29 29 (** {1 Operations} *) 30 30
+19 -19
lib/protocol.ml
··· 58 58 sign; 59 59 timestamp; 60 60 }) 61 - |> Object.mem "from" string ~enc:(fun h -> h.from_) 62 - |> Object.mem "messageId" string ~enc:(fun h -> h.message_id) 63 - |> Object.mem "method" string ~enc:(fun h -> h.method_) 64 - |> Object.mem "namespace" string ~enc:(fun h -> h.namespace) 65 - |> Object.mem "payloadVersion" int ~enc:(fun h -> h.payload_version) 66 - |> Object.mem "sign" string ~enc:(fun h -> h.sign) 67 - |> Object.mem "timestamp" int ~enc:(fun h -> h.timestamp) 68 - |> Object.finish 61 + |> Object.member "from" string ~enc:(fun h -> h.from_) 62 + |> Object.member "messageId" string ~enc:(fun h -> h.message_id) 63 + |> Object.member "method" string ~enc:(fun h -> h.method_) 64 + |> Object.member "namespace" string ~enc:(fun h -> h.namespace) 65 + |> Object.member "payloadVersion" int ~enc:(fun h -> h.payload_version) 66 + |> Object.member "sign" string ~enc:(fun h -> h.sign) 67 + |> Object.member "timestamp" int ~enc:(fun h -> h.timestamp) 68 + |> Object.seal 69 69 70 70 (** Build a request header *) 71 71 let header ~method_ ~namespace = ··· 87 87 let request_codec payload_codec = 88 88 let open Json.Codec in 89 89 Object.map ~kind:"request" (fun header payload -> { header; payload }) 90 - |> Object.mem "header" header_codec ~enc:(fun r -> r.header) 91 - |> Object.mem "payload" payload_codec ~enc:(fun r -> r.payload) 92 - |> Object.finish 90 + |> Object.member "header" header_codec ~enc:(fun r -> r.header) 91 + |> Object.member "payload" payload_codec ~enc:(fun r -> r.payload) 92 + |> Object.seal 93 93 94 94 type 'a response = { resp_header : header; resp_payload : 'a } 95 95 ··· 97 97 let open Json.Codec in 98 98 Object.map ~kind:"response" (fun resp_header resp_payload -> 99 99 { resp_header; resp_payload }) 100 - |> Object.mem "header" header_codec ~enc:(fun r -> r.resp_header) 101 - |> Object.mem "payload" payload_codec ~enc:(fun r -> r.resp_payload) 102 - |> Object.finish 100 + |> Object.member "header" header_codec ~enc:(fun r -> r.resp_header) 101 + |> Object.member "payload" payload_codec ~enc:(fun r -> r.resp_payload) 102 + |> Object.seal 103 103 104 104 (** {1 Common Payload Codecs} *) 105 105 106 106 (** Empty payload for commands that don't need data *) 107 107 let empty_payload_codec : unit Json.codec = 108 108 let open Json.Codec in 109 - Object.map ~kind:"empty" () |> Object.skip_unknown |> Object.finish 109 + Object.map ~kind:"empty" () |> Object.skip_unknown |> Object.seal 110 110 111 111 type enable_payload = { enable : int } 112 112 (** Enable payload for SET commands *) ··· 114 114 let enable_payload_codec = 115 115 let open Json.Codec in 116 116 Object.map ~kind:"enable_payload" (fun enable -> { enable }) 117 - |> Object.mem "enable" int ~enc:(fun p -> p.enable) 118 - |> Object.finish 117 + |> Object.member "enable" int ~enc:(fun p -> p.enable) 118 + |> Object.seal 119 119 120 120 (** {1 JSON Helpers} *) 121 121 ··· 178 178 let payload_codec ~key codec = 179 179 let open Json.Codec in 180 180 Object.map ~kind:(key ^ "_payload") Fun.id 181 - |> Object.mem key codec ~enc:Fun.id 182 - |> Object.skip_unknown |> Object.finish 181 + |> Object.member key codec ~enc:Fun.id 182 + |> Object.skip_unknown |> Object.seal 183 183 184 184 (** GET request for a device feature. Example: 185 185 [Feature.get ~namespace:"Appliance.Control.Electricity" ~key:"electricity"
+2 -2
lib/runtime.ml
··· 13 13 let codec = 14 14 Json.Codec.Object.map ~kind:"runtime" (fun signal -> 15 15 { signal = Option.value ~default:0 signal }) 16 - |> Json.Codec.Object.opt_mem "signal" Json.Codec.int ~enc:(fun r -> 16 + |> Json.Codec.Object.opt_member "signal" Json.Codec.int ~enc:(fun r -> 17 17 Some r.signal) 18 - |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.finish 18 + |> Json.Codec.Object.skip_unknown |> Json.Codec.Object.seal 19 19 20 20 let pp ppf t = Fmt.pf ppf "signal: %d%%" t.signal 21 21
+10 -10
lib/timers.ml
··· 35 35 end_time = Option.value ~default:0 end_time; 36 36 duration = Option.value ~default:0 duration; 37 37 }) 38 - |> Object.opt_mem "onoff" int ~enc:(fun c -> Some c.onoff) 39 - |> Object.opt_mem "end" int ~enc:(fun c -> Some c.end_time) 40 - |> Object.opt_mem "duration" int ~enc:(fun c -> Some c.duration) 41 - |> Object.skip_unknown |> Object.finish 38 + |> Object.opt_member "onoff" int ~enc:(fun c -> Some c.onoff) 39 + |> Object.opt_member "end" int ~enc:(fun c -> Some c.end_time) 40 + |> Object.opt_member "duration" int ~enc:(fun c -> Some c.duration) 41 + |> Object.skip_unknown |> Object.seal 42 42 43 43 let timer_codec = 44 44 let open Json.Codec in ··· 48 48 timer_type = Option.value ~default:1 timer_type; 49 49 down; 50 50 }) 51 - |> Object.opt_mem "channel" int ~enc:(fun t -> Some t.channel) 52 - |> Object.opt_mem "type" int ~enc:(fun t -> Some t.timer_type) 53 - |> Object.opt_mem "down" countdown_codec ~enc:(fun t -> t.down) 54 - |> Object.skip_unknown |> Object.finish 51 + |> Object.opt_member "channel" int ~enc:(fun t -> Some t.channel) 52 + |> Object.opt_member "type" int ~enc:(fun t -> Some t.timer_type) 53 + |> Object.opt_member "down" countdown_codec ~enc:(fun t -> t.down) 54 + |> Object.skip_unknown |> Object.seal 55 55 56 56 type payload = { timerx : t list } 57 57 (** Payload with timer list *) ··· 59 59 let payload_codec = 60 60 let open Json.Codec in 61 61 Object.map ~kind:"timerx_payload" (fun t -> { timerx = t }) 62 - |> Object.mem "timerx" (list timer_codec) ~enc:(fun p -> p.timerx) 63 - |> Object.skip_unknown |> Object.finish 62 + |> Object.member "timerx" (list timer_codec) ~enc:(fun p -> p.timerx) 63 + |> Object.skip_unknown |> Object.seal 64 64 65 65 (** {1 Operations} *) 66 66
+17 -17
lib/triggers.ml
··· 33 33 let rule_codec = 34 34 let open Json.Codec in 35 35 Object.map ~kind:"rule" (fun week duration -> { week; duration }) 36 - |> Object.mem "week" int ~enc:(fun r -> r.week) 37 - |> Object.mem "duration" int ~enc:(fun r -> r.duration) 38 - |> Object.skip_unknown |> Object.finish 36 + |> Object.member "week" int ~enc:(fun r -> r.week) 37 + |> Object.member "duration" int ~enc:(fun r -> r.duration) 38 + |> Object.skip_unknown |> Object.seal 39 39 40 40 let codec = 41 41 let open Json.Codec in ··· 50 50 create_time = Option.value ~default:0 create_time; 51 51 rule; 52 52 }) 53 - |> Object.mem "id" string ~enc:id 54 - |> Object.opt_mem "type" int ~enc:(fun t -> Some t.trigger_type) 55 - |> Object.mem "enable" int ~enc:(fun t -> if t.enabled then 1 else 0) 56 - |> Object.opt_mem "channel" int ~enc:(fun t -> Some t.channel) 57 - |> Object.opt_mem "alias" string ~enc:(fun t -> 53 + |> Object.member "id" string ~enc:id 54 + |> Object.opt_member "type" int ~enc:(fun t -> Some t.trigger_type) 55 + |> Object.member "enable" int ~enc:(fun t -> if t.enabled then 1 else 0) 56 + |> Object.opt_member "channel" int ~enc:(fun t -> Some t.channel) 57 + |> Object.opt_member "alias" string ~enc:(fun t -> 58 58 if t.alias = "" then None else Some t.alias) 59 - |> Object.opt_mem "createTime" int ~enc:(fun t -> Some t.create_time) 60 - |> Object.mem "rule" rule_codec ~enc:(fun t -> t.rule) 61 - |> Object.skip_unknown |> Object.finish 59 + |> Object.opt_member "createTime" int ~enc:(fun t -> Some t.create_time) 60 + |> Object.member "rule" rule_codec ~enc:(fun t -> t.rule) 61 + |> Object.skip_unknown |> Object.seal 62 62 63 63 type payload = { triggerx : t list } 64 64 65 65 let payload_codec = 66 66 let open Json.Codec in 67 67 Object.map ~kind:"triggerx_payload" (fun t -> { triggerx = t }) 68 - |> Object.mem "triggerx" (list codec) ~enc:(fun p -> p.triggerx) 69 - |> Object.skip_unknown |> Object.finish 68 + |> Object.member "triggerx" (list codec) ~enc:(fun p -> p.triggerx) 69 + |> Object.skip_unknown |> Object.seal 70 70 71 71 type single_payload = { trigger : t } 72 72 (** Single trigger payload for SET operations *) ··· 74 74 let single_payload_codec = 75 75 let open Json.Codec in 76 76 Object.map ~kind:"single_triggerx_payload" (fun t -> { trigger = t }) 77 - |> Object.mem "triggerx" codec ~enc:(fun p -> p.trigger) 78 - |> Object.skip_unknown |> Object.finish 77 + |> Object.member "triggerx" codec ~enc:(fun p -> p.trigger) 78 + |> Object.skip_unknown |> Object.seal 79 79 80 80 type digest_payload = { digest : t list } 81 81 (** Digest payload uses "digest" field instead of "triggerx" *) ··· 83 83 let digest_payload_codec = 84 84 let open Json.Codec in 85 85 Object.map ~kind:"digest_payload" (fun d -> { digest = d }) 86 - |> Object.mem "digest" (list codec) ~enc:(fun p -> p.digest) 87 - |> Object.skip_unknown |> Object.finish 86 + |> Object.member "digest" (list codec) ~enc:(fun p -> p.digest) 87 + |> Object.skip_unknown |> Object.seal 88 88 89 89 (** {1 Operations} *) 90 90
+2 -2
meross.opam
··· 20 20 "fmt" {>= "0.9"} 21 21 "ipaddr" {>= "5.0"} 22 22 "json" {>= "0.1.0"} 23 - "bytesrw" {>= "0.1.0"} 24 23 "logs" {>= "0.7"} 25 24 "crypto-rng" {>= "1.0"} 26 25 "ptime" {>= "1.0"} 27 26 "uri" {>= "4.0"} 28 27 "alcotest" {with-test} 29 - "crowbar" {with-test} 28 + "alcobar" {with-test} 30 29 "odoc" {with-doc} 30 + "loc" 31 31 ] 32 32 build: [ 33 33 ["dune" "subst"] {dev}