├── .github └── workflows │ └── go.yml ├── .gitignore ├── .golangci.yaml ├── LICENSE ├── Makefile ├── README.md ├── bootstrap ├── bootstrap.go ├── handler.go └── option.go ├── core ├── core.go ├── device.go ├── inactivitymonitor.go ├── manager.go └── observe.go ├── encoding ├── corelink.go ├── corelink_test.go ├── encoding.go ├── opaque.go ├── opaque_test.go ├── textplain.go ├── textplain_test.go ├── tlv.go └── tlv_test.go ├── examples ├── bootstrap │ └── main.go ├── content-format │ └── main.go ├── dtls │ └── main.go ├── observe │ └── main.go ├── readwrite │ └── main.go └── simple │ └── main.go ├── go.mod ├── go.sum ├── node ├── definition │ ├── 0.xml │ ├── 1.xml │ ├── 10.xml │ ├── 10241.xml │ ├── 10242.xml │ ├── 10243.xml │ ├── 10244.xml │ ├── 10245.xml │ ├── 10246.xml │ ├── 10247.xml │ ├── 10248.xml │ ├── 10249.xml │ ├── 10250.xml │ ├── 10251.xml │ ├── 10252.xml │ ├── 10253.xml │ ├── 10254.xml │ ├── 10255.xml │ ├── 10256.xml │ ├── 10257.xml │ ├── 10258.xml │ ├── 10259.xml │ ├── 10260.xml │ ├── 10262.xml │ ├── 10263.xml │ ├── 10264.xml │ ├── 10265.xml │ ├── 10266.xml │ ├── 10267.xml │ ├── 10268.xml │ ├── 10269.xml │ ├── 10270.xml │ ├── 10271.xml │ ├── 10272.xml │ ├── 10273.xml │ ├── 10274.xml │ ├── 10275.xml │ ├── 10276.xml │ ├── 10277.xml │ ├── 10278.xml │ ├── 10279.xml │ ├── 10280.xml │ ├── 10281.xml │ ├── 10282.xml │ ├── 10283.xml │ ├── 10284.xml │ ├── 10286.xml │ ├── 10290.xml │ ├── 10291.xml │ ├── 10292.xml │ ├── 10299.xml │ ├── 10300.xml │ ├── 10308.xml │ ├── 10309.xml │ ├── 10311.xml │ ├── 10313.xml │ ├── 10314.xml │ ├── 10315.xml │ ├── 10316.xml │ ├── 10318.xml │ ├── 10319.xml │ ├── 10320.xml │ ├── 10322.xml │ ├── 10323.xml │ ├── 10324.xml │ ├── 10326.xml │ ├── 10327.xml │ ├── 10328.xml │ ├── 10329.xml │ ├── 10330.xml │ ├── 10331.xml │ ├── 10332.xml │ ├── 10333.xml │ ├── 10334.xml │ ├── 10335.xml │ ├── 10336.xml │ ├── 10337.xml │ ├── 10338.xml │ ├── 10339.xml │ ├── 10340.xml │ ├── 10341.xml │ ├── 10342.xml │ ├── 10343.xml │ ├── 10344.xml │ ├── 10345.xml │ ├── 10346.xml │ ├── 10347.xml │ ├── 10348.xml │ ├── 10349.xml │ ├── 10350.xml │ ├── 10351.xml │ ├── 10352.xml │ ├── 10353.xml │ ├── 10354.xml │ ├── 10355.xml │ ├── 10356.xml │ ├── 10357.xml │ ├── 10358.xml │ ├── 10359.xml │ ├── 10360.xml │ ├── 10361.xml │ ├── 10362.xml │ ├── 10363.xml │ ├── 10364.xml │ ├── 10365.xml │ ├── 10366.xml │ ├── 10368.xml │ ├── 10369.xml │ ├── 10371.xml │ ├── 11.xml │ ├── 12.xml │ ├── 13.xml │ ├── 14.xml │ ├── 15.xml │ ├── 16.xml │ ├── 18830.xml │ ├── 18831.xml │ ├── 19.xml │ ├── 2.xml │ ├── 20.xml │ ├── 2048.xml │ ├── 2049.xml │ ├── 2050.xml │ ├── 2051.xml │ ├── 2052.xml │ ├── 2053.xml │ ├── 2054.xml │ ├── 2055.xml │ ├── 2056.xml │ ├── 2057.xml │ ├── 21.xml │ ├── 22.xml │ ├── 23.xml │ ├── 24.xml │ ├── 25.xml │ ├── 26.xml │ ├── 27.xml │ ├── 3.xml │ ├── 3200.xml │ ├── 3201.xml │ ├── 3202.xml │ ├── 3203.xml │ ├── 3300.xml │ ├── 3301.xml │ ├── 3302.xml │ ├── 3303.xml │ ├── 3304.xml │ ├── 3305.xml │ ├── 3306.xml │ ├── 3308.xml │ ├── 3310.xml │ ├── 3311.xml │ ├── 3312.xml │ ├── 3313.xml │ ├── 3314.xml │ ├── 3315.xml │ ├── 3316.xml │ ├── 3317.xml │ ├── 3318.xml │ ├── 3319.xml │ ├── 3320.xml │ ├── 3321.xml │ ├── 3322.xml │ ├── 3323.xml │ ├── 3324.xml │ ├── 3325.xml │ ├── 3326.xml │ ├── 3327.xml │ ├── 3328.xml │ ├── 3329.xml │ ├── 3330.xml │ ├── 3331.xml │ ├── 3332.xml │ ├── 3333.xml │ ├── 3334.xml │ ├── 3335.xml │ ├── 3336.xml │ ├── 3337.xml │ ├── 3338.xml │ ├── 3339.xml │ ├── 3340.xml │ ├── 3341.xml │ ├── 3342.xml │ ├── 3343.xml │ ├── 3344.xml │ ├── 3345.xml │ ├── 3346.xml │ ├── 3347.xml │ ├── 3348.xml │ ├── 3349.xml │ ├── 3350.xml │ ├── 3351.xml │ ├── 3352.xml │ ├── 3353.xml │ ├── 3354.xml │ ├── 3355.xml │ ├── 3356.xml │ ├── 3357.xml │ ├── 3358.xml │ ├── 3359.xml │ ├── 3360.xml │ ├── 3361.xml │ ├── 3362.xml │ ├── 3363.xml │ ├── 3364.xml │ ├── 3365.xml │ ├── 3366.xml │ ├── 3367.xml │ ├── 3368.xml │ ├── 3369.xml │ ├── 3370.xml │ ├── 3371.xml │ ├── 3372.xml │ ├── 3373.xml │ ├── 3374.xml │ ├── 3375.xml │ ├── 3376.xml │ ├── 3377.xml │ ├── 3378.xml │ ├── 3379.xml │ ├── 3380.xml │ ├── 3381.xml │ ├── 3382.xml │ ├── 3383.xml │ ├── 3384.xml │ ├── 3385.xml │ ├── 3386.xml │ ├── 3387.xml │ ├── 3388.xml │ ├── 3389.xml │ ├── 3390.xml │ ├── 3391.xml │ ├── 3392.xml │ ├── 3393.xml │ ├── 3394.xml │ ├── 3395.xml │ ├── 3396.xml │ ├── 3397.xml │ ├── 3398.xml │ ├── 3399.xml │ ├── 3400.xml │ ├── 3401.xml │ ├── 3402.xml │ ├── 3403.xml │ ├── 3404.xml │ ├── 3405.xml │ ├── 3406.xml │ ├── 3407.xml │ ├── 3408.xml │ ├── 3410.xml │ ├── 3411.xml │ ├── 3412.xml │ ├── 3413.xml │ ├── 3414.xml │ ├── 3415.xml │ ├── 3416.xml │ ├── 3417.xml │ ├── 3418.xml │ ├── 3419.xml │ ├── 3420.xml │ ├── 3421.xml │ ├── 3423.xml │ ├── 3424.xml │ ├── 3425.xml │ ├── 3426.xml │ ├── 3427.xml │ ├── 3428.xml │ ├── 3429.xml │ ├── 3430.xml │ ├── 3431.xml │ ├── 3432.xml │ ├── 3433.xml │ ├── 3434.xml │ ├── 3435.xml │ ├── 3436.xml │ ├── 3437.xml │ ├── 3438.xml │ ├── 3439.xml │ ├── 4.xml │ ├── 5.xml │ ├── 500.xml │ ├── 501.xml │ ├── 502.xml │ ├── 503.xml │ ├── 6.xml │ ├── 7.xml │ ├── 8.xml │ └── 9.xml ├── node.go ├── node_test.go ├── object.go ├── object_test.go ├── path.go ├── path_test.go ├── registry.go ├── registry_test.go └── resource.go ├── registration ├── config.go └── handler.go └── server ├── config.go ├── dtls.go ├── server.go └── udp.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v3 21 | with: 22 | go-version: 1.18 23 | 24 | - name: Build 25 | run: go build -v ./... 26 | 27 | - name: Test 28 | run: go test -v ./... 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - unused 5 | - ineffassign 6 | - staticcheck 7 | 8 | linters-settings: 9 | staticcheck: 10 | go: "1.18" 11 | checks: [ 12 | "all", 13 | "-ST1*" 14 | ] 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run_simple run_mqtt 2 | 3 | run_simple: 4 | go run examples/simple/*.go 5 | 6 | run_mqtt: 7 | go run examples/lwm2mqtt/*.go 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lwm2m 2 | 3 | ![build](https://github.com/yplam/lwm2m/actions/workflows/go.yml/badge.svg) ![Go Report Card](https://goreportcard.com/badge/github.com/yplam/lwm2m) 4 | 5 | A simple and lightweight ( but not full feature ) lwm2m server aim to run on edge router. 6 | 7 | ## Features 8 | 9 | - [ ] Bootstrap Interface 10 | - [x] Client Registration interface. 11 | * [x] Register 12 | * [x] Update 13 | * [x] Deregister 14 | - [ ] Device Management and Service Enablement interface. 15 | * [x] Read Operation, Read Resource, Read Object 16 | * [x] Write Operation, Write Resource, Write Object Instance 17 | * [x] Execute Operation 18 | * [x] Discover Operation 19 | * [x] Create Operation 20 | * [x] Delete Operation 21 | * [ ] Write-Attributes Operation 22 | * [ ] Read-Composite Operation 23 | * [ ] Write-Composite Operation 24 | - [x] Information Reporting interface. 25 | * [x] Observe Operation, Observe Resource, Observe Object 26 | * [x] Cancel Observation Operation 27 | * [ ] Observe-Composite Operation 28 | * [ ] Cancel Observation-Composite Operation 29 | * [ ] Send Operation 30 | - [ ] Data formats 31 | * [ ] Plain Text 32 | * [ ] Opaque 33 | * [ ] CBOR 34 | * [x] TLV 35 | * [ ] SenML JSON 36 | * [ ] SenML CBOR 37 | * [ ] LwM2M JSON 38 | - [ ] Security 39 | * [ ] DTLS with Certificates 40 | * [x] DTLS with PSK, only support DTLS 1.2 41 | - [x] Transport 42 | * [x] UDP transport support. 43 | * [ ] TCP transport support.(Some features may not work properly) 44 | - [ ] Tested with clients 45 | * [x] Leshan client: coap, coaps + psk 46 | * [x] Anjay client running on ESP32: coap 47 | * [x] Anjay client running on Linux: coap, coaps + psk 48 | * [x] Zephyr LWM2M client running on nrf52840 with w5500 ethernet: coap, coaps + psk 49 | * [ ] Zephyr LWM2M client running on nrf52840 with Openthread 50 | 51 | ## Installation 52 | 53 | You need a working Go environment. 54 | 55 | ``` 56 | go get github.com/yplam/lwm2m 57 | ``` 58 | 59 | ## Getting Started 60 | 61 | ```go 62 | package main 63 | 64 | import ( 65 | "github.com/yplam/lwm2m/core" 66 | "github.com/yplam/lwm2m/registration" 67 | "github.com/yplam/lwm2m/server" 68 | "log" 69 | ) 70 | 71 | func main() { 72 | r := server.DefaultRouter() 73 | deviceManager := core.DefaultManager() 74 | registration.EnableHandler(r, deviceManager) 75 | err := server.ListenAndServe(r, 76 | server.EnableUDPListener("udp", ":5683"), 77 | ) 78 | if err != nil { 79 | log.Printf("serve lwm2m with err: %v", err) 80 | } 81 | } 82 | 83 | ``` 84 | 85 | ## Test Commands 86 | 87 | leshan client 88 | 89 | ```shell 90 | # coap 91 | java -jar leshan-client-demo.jar --server-url=localhost:5683 92 | 93 | # coaps 94 | java -jar leshan-client-demo.jar --server-url=localhost:5684 -i 123 --psk-key=000102030405060708090a0b0c0d0e0f 95 | ``` 96 | anjay client 97 | ```shell 98 | # coap 99 | ./output/bin/demo --endpoint-name $(hostname) --server-uri coap://127.0.0.1:5683 100 | 101 | # coap + tcp 102 | ./output/bin/demo --endpoint-name $(hostname) --server-uri coap+tcp://127.0.0.1:5685 103 | 104 | # coaps 105 | ./output/bin/demo --endpoint-name $(hostname) --server-uri coaps://127.0.0.1:5684 --security-mode psk --identity 666f6f --key 000102030405060708090a0b0c0d0e0f --ciphersuites 49320 --tls-version TLSv1.2 106 | ``` 107 | 108 | ## License 109 | 110 | Apache License Version 2.0. See the [LICENSE](LICENSE) file for details. -------------------------------------------------------------------------------- /bootstrap/bootstrap.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/plgd-dev/go-coap/v3/message" 7 | "github.com/plgd-dev/go-coap/v3/message/codes" 8 | "github.com/plgd-dev/go-coap/v3/mux" 9 | "github.com/yplam/lwm2m/core" 10 | "github.com/yplam/lwm2m/encoding" 11 | "github.com/yplam/lwm2m/node" 12 | "io" 13 | ) 14 | 15 | type Client struct { 16 | Endpoint string 17 | conn mux.Conn 18 | selectedCt message.MediaType 19 | } 20 | 21 | func (c *Client) ContentType() message.MediaType { 22 | return c.selectedCt 23 | } 24 | 25 | func (c *Client) SetContentType(ct message.MediaType) { 26 | c.selectedCt = ct 27 | } 28 | 29 | func (c *Client) finish(ctx context.Context) (err error) { 30 | res, err := c.conn.Post(ctx, "/bs", c.selectedCt, nil) 31 | if err != nil { 32 | return err 33 | } 34 | if res.Code() != codes.Changed { 35 | return fmt.Errorf("unexpected response code: %v", res.Code()) 36 | } 37 | return nil 38 | } 39 | 40 | func (c *Client) Conn() mux.Conn { 41 | return c.conn 42 | } 43 | 44 | func (c *Client) Write(ctx context.Context, path node.Path, val ...node.Node) error { 45 | msg, err := node.EncodeMessage(c.selectedCt, val) 46 | if err != nil { 47 | return err 48 | } 49 | resp, err := c.conn.Put(ctx, path.String(), c.selectedCt, msg) 50 | if err != nil { 51 | return err 52 | } 53 | if resp.Code() != codes.Changed { 54 | return fmt.Errorf("unexpected response code: %v", resp.Code()) 55 | } 56 | return nil 57 | } 58 | 59 | func (c *Client) Read(ctx context.Context, path node.Path) ([]node.Node, error) { 60 | 61 | // accept 62 | buf := make([]byte, 2) 63 | _, _ = message.EncodeUint32(buf, uint32(c.selectedCt)) 64 | acceptOption := message.Option{ 65 | ID: message.Accept, 66 | Value: buf[:], 67 | } 68 | 69 | msg, err := c.conn.Get(ctx, path.String(), acceptOption) 70 | if err != nil { 71 | return nil, err 72 | } 73 | if msg.Code() != codes.Content { 74 | return nil, fmt.Errorf("unexpected response code: %v", msg.Code()) 75 | } 76 | if msg.Body() == nil { 77 | return nil, core.ErrEmptyBody 78 | } 79 | return node.DecodeMessage(path, msg) 80 | } 81 | 82 | func (c *Client) Discover(ctx context.Context, path node.Path) ([]*encoding.CoreLink, error) { 83 | 84 | buf := make([]byte, 2) 85 | l, _ := message.EncodeUint32(buf, uint32(message.AppLinkFormat)) 86 | r, err := c.conn.Get(ctx, path.String(), 87 | message.Option{ 88 | ID: message.Accept, 89 | Value: buf[:l], 90 | }) 91 | if err != nil { 92 | return nil, err 93 | } 94 | if r.Code() != codes.Content { 95 | return nil, fmt.Errorf("unexpected response code: %v", r.Code()) 96 | } 97 | links := make([]*encoding.CoreLink, 0) 98 | if r.Body() != nil { 99 | if b, err := io.ReadAll(r.Body()); err == nil { 100 | links, _ = encoding.CoreLinksFromString(string(b)) 101 | } 102 | } 103 | return links, nil 104 | } 105 | 106 | func (c *Client) Delete(ctx context.Context, path node.Path) (err error) { 107 | resp, err := c.conn.Delete(ctx, path.String()) 108 | if err != nil { 109 | return err 110 | } 111 | if resp.Code() != codes.Deleted { 112 | return fmt.Errorf("unexpected response code: %v", resp.Code()) 113 | } 114 | return nil 115 | } 116 | 117 | type Provider interface { 118 | HandleBsRequest(ctx context.Context, device *Client) error 119 | } 120 | -------------------------------------------------------------------------------- /bootstrap/handler.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "github.com/pion/logging" 8 | "github.com/plgd-dev/go-coap/v3/message" 9 | "github.com/plgd-dev/go-coap/v3/message/codes" 10 | "github.com/plgd-dev/go-coap/v3/mux" 11 | "github.com/yplam/lwm2m/core" 12 | "strconv" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | const DefaultBootstrapTimeout = 30 * time.Second 18 | 19 | var DefaultContentType = message.AppLwm2mTLV 20 | 21 | type Handler struct { 22 | timeout time.Duration 23 | logger logging.LeveledLogger 24 | provider Provider 25 | } 26 | 27 | func (h *Handler) ServeCOAP(w mux.ResponseWriter, r *mux.Message) { 28 | h.logger.Debugf("bootstrap serve coap: %v", r) 29 | 30 | ep := "" // endpoint, optional 31 | pctOpt := strconv.FormatInt(int64(message.AppLwm2mTLV), 10) // preferred content-type, optional 32 | opts := r.Options() 33 | for _, o := range opts { 34 | h.logger.Debugf("option: %s, %d", string(o.Value), o.ID) 35 | if o.ID != message.URIQuery { 36 | continue 37 | } 38 | so := strings.Split(string(o.Value), "=") 39 | if len(so) != 2 { 40 | w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("bad uri query"))) 41 | return 42 | } 43 | switch so[0] { 44 | case "ep": 45 | ep = so[1] 46 | case "pct": 47 | pctOpt = so[1] 48 | default: 49 | w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte("unsupported option"))) 50 | return 51 | } 52 | } 53 | 54 | ipct, err := strconv.ParseUint(pctOpt, 10, 16) 55 | if err != nil { 56 | err = fmt.Errorf("parse pct option: %w", err) 57 | w.SetResponse(codes.BadRequest, message.TextPlain, bytes.NewReader([]byte(err.Error()))) 58 | return 59 | } 60 | pct := message.MediaType(ipct) 61 | 62 | // select content type 63 | switch pct { 64 | case message.AppLwm2mTLV: 65 | case message.TextPlain: 66 | case message.AppOctets: 67 | default: 68 | h.logger.Debugf("client prefer unsupported(yet) content-type %s", pct.String()) 69 | h.logger.Debugf("using %s as default", DefaultContentType.String()) 70 | pct = DefaultContentType 71 | } 72 | 73 | client := &Client{ 74 | Endpoint: ep, 75 | conn: w.Conn(), 76 | selectedCt: pct, 77 | } 78 | 79 | core.SetLifetime(client.conn, h.timeout) 80 | 81 | err = w.SetResponse(codes.Changed, client.selectedCt, nil) 82 | if err != nil { 83 | h.logger.Errorf("error sending response to bootstrap request: %v", err) 84 | return 85 | } 86 | 87 | // limit bootstrap procedure by timeout with ctx 88 | ctx, cancel := context.WithTimeout(context.TODO(), h.timeout) 89 | go func() { 90 | defer func() { 91 | // send finish message in any case 92 | err := client.finish(context.TODO()) 93 | if err != nil { 94 | h.logger.Errorf("error sending bootstrap-finish: %v", err) 95 | } 96 | cancel() 97 | }() 98 | err := h.provider.HandleBsRequest(ctx, client) 99 | if err != nil { 100 | h.logger.Errorf("bootstrap provider err: %v", err) 101 | return 102 | } 103 | }() 104 | } 105 | 106 | func EnableHandler(r *mux.Router, p Provider, opts ...Option) { 107 | h := &Handler{ 108 | provider: p, 109 | timeout: DefaultBootstrapTimeout, 110 | } 111 | for _, opt := range opts { 112 | opt(h) 113 | } 114 | if h.logger == nil { 115 | lf := logging.NewDefaultLoggerFactory() 116 | h.logger = lf.NewLogger("bootstrap") 117 | } 118 | _ = r.Handle("/bs", h) 119 | } 120 | -------------------------------------------------------------------------------- /bootstrap/option.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "github.com/pion/logging" 5 | "time" 6 | ) 7 | 8 | type Option func(h *Handler) 9 | 10 | func WithLogger(l logging.LeveledLogger) Option { 11 | return func(h *Handler) { 12 | h.logger = l 13 | } 14 | } 15 | 16 | func WithBootstrapTimeout(timeout time.Duration) Option { 17 | return func(h *Handler) { 18 | h.timeout = timeout 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/core.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/yplam/lwm2m/encoding" 7 | "github.com/yplam/lwm2m/node" 8 | ) 9 | 10 | var ( 11 | ErrUnexpectedResponseCode = errors.New("unexpected response code") 12 | ErrEmptyBody = errors.New("empty body") 13 | ) 14 | 15 | type Observer interface { 16 | ObserveSync(p node.Path, onMsg ObserveFunc) error 17 | Observe(p node.Path, onMsg ObserveFunc) error 18 | ObserveObject(p node.Path, onMsg ObserveObjectFunc) error 19 | ObserveResource(p node.Path, onMsg ObserveResourceFunc) error 20 | CancelObserve(p node.Path) error 21 | } 22 | 23 | type DeviceManager interface { 24 | Read(ctx context.Context, p node.Path) ([]node.Node, error) 25 | ReadObject(ctx context.Context, p node.Path) (*node.Object, error) 26 | ReadResource(ctx context.Context, p node.Path) (*node.Resource, error) 27 | Write(ctx context.Context, p node.Path, val ...node.Node) error 28 | WriteResource(ctx context.Context, p node.Path, val *node.Resource) error 29 | WriteObjectInstance(ctx context.Context, p node.Path, val *node.ObjectInstance) error 30 | Discover(ctx context.Context, p node.Path) ([]*encoding.CoreLink, error) 31 | } 32 | -------------------------------------------------------------------------------- /core/inactivitymonitor.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | dtlsServer "github.com/plgd-dev/go-coap/v3/dtls/server" 6 | "github.com/plgd-dev/go-coap/v3/mux" 7 | "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" 8 | "github.com/plgd-dev/go-coap/v3/options" 9 | tcpClient "github.com/plgd-dev/go-coap/v3/tcp/client" 10 | tcpServer "github.com/plgd-dev/go-coap/v3/tcp/server" 11 | udpClient "github.com/plgd-dev/go-coap/v3/udp/client" 12 | udpServer "github.com/plgd-dev/go-coap/v3/udp/server" 13 | "sync/atomic" 14 | "time" 15 | ) 16 | 17 | // InactivityMonitorOpt notifies when a connection was inactive for a given duration. 18 | type InactivityMonitorOpt[C options.OnInactiveFunc] struct { 19 | onInactive C 20 | } 21 | 22 | func (o InactivityMonitorOpt[C]) toTCPCreateInactivityMonitor(onInactive options.TCPOnInactive) func() tcpClient.InactivityMonitor { 23 | return func() tcpClient.InactivityMonitor { 24 | return NewInactivityMonitor(onInactive) 25 | } 26 | } 27 | 28 | func (o InactivityMonitorOpt[C]) toUDPCreateInactivityMonitor(onInactive options.UDPOnInactive) func() udpClient.InactivityMonitor { 29 | return func() udpClient.InactivityMonitor { 30 | return NewInactivityMonitor(onInactive) 31 | } 32 | } 33 | 34 | func (o InactivityMonitorOpt[C]) TCPServerApply(cfg *tcpServer.Config) { 35 | switch onInactive := any(o.onInactive).(type) { 36 | case options.TCPOnInactive: 37 | cfg.CreateInactivityMonitor = o.toTCPCreateInactivityMonitor(onInactive) 38 | } 39 | } 40 | 41 | func (o InactivityMonitorOpt[C]) UDPServerApply(cfg *udpServer.Config) { 42 | switch onInactive := any(o.onInactive).(type) { 43 | case options.UDPOnInactive: 44 | cfg.CreateInactivityMonitor = o.toUDPCreateInactivityMonitor(onInactive) 45 | default: 46 | } 47 | } 48 | 49 | func (o InactivityMonitorOpt[C]) DTLSServerApply(cfg *dtlsServer.Config) { 50 | switch onInactive := any(o.onInactive).(type) { 51 | case options.UDPOnInactive: 52 | cfg.CreateInactivityMonitor = o.toUDPCreateInactivityMonitor(onInactive) 53 | } 54 | } 55 | 56 | // WithInactivityMonitor set deadline's for read operations over client connection. 57 | func WithInactivityMonitor[C options.OnInactiveFunc](onInactive C) InactivityMonitorOpt[C] { 58 | return InactivityMonitorOpt[C]{ 59 | onInactive: onInactive, 60 | } 61 | } 62 | 63 | func NewInactivityMonitor[C inactivity.Conn](onInactive inactivity.OnInactiveFunc[C]) *InactivityMonitor[C] { 64 | m := &InactivityMonitor[C]{ 65 | onInactive: onInactive, 66 | } 67 | m.Notify() 68 | return m 69 | } 70 | 71 | type InactivityMonitor[C inactivity.Conn] struct { 72 | lastActivity atomic.Value 73 | onInactive inactivity.OnInactiveFunc[C] 74 | } 75 | 76 | func (m *InactivityMonitor[C]) Notify() { 77 | m.lastActivity.Store(time.Now()) 78 | } 79 | 80 | func (m *InactivityMonitor[C]) LastActivity() time.Time { 81 | if t, ok := m.lastActivity.Load().(time.Time); ok { 82 | return t 83 | } 84 | return time.Time{} 85 | } 86 | 87 | func CloseConn(cc inactivity.Conn) { 88 | // call cc.Close() directly to check and handle error if necessary 89 | _ = cc.Close() 90 | } 91 | 92 | func New[C inactivity.Conn](onInactive inactivity.OnInactiveFunc[C]) *InactivityMonitor[C] { 93 | m := &InactivityMonitor[C]{ 94 | onInactive: onInactive, 95 | } 96 | m.Notify() 97 | return m 98 | } 99 | 100 | type lifetimeCtxKeyType string 101 | 102 | const lifetimeCtxKey lifetimeCtxKeyType = "core_lifetime" 103 | 104 | func WithLifetime(ctx context.Context, lifetime time.Duration) context.Context { 105 | return context.WithValue(ctx, lifetimeCtxKey, lifetime) 106 | } 107 | 108 | func SetLifetime(c mux.Conn, d time.Duration) { 109 | c.SetContextValue(lifetimeCtxKey, d) 110 | } 111 | 112 | func GetLifetime(ctx context.Context) *time.Duration { 113 | lifetime, ok := ctx.Value(lifetimeCtxKey).(time.Duration) 114 | if !ok { 115 | // Log this issue 116 | return nil 117 | } 118 | return &lifetime 119 | } 120 | 121 | func (m *InactivityMonitor[C]) CheckInactivity(now time.Time, cc C) { 122 | if m.onInactive == nil { 123 | return 124 | } 125 | duration := time.Second * 2 126 | if lifetime := GetLifetime(cc.Context()); lifetime != nil { 127 | duration = *lifetime 128 | } 129 | if now.After(m.LastActivity().Add(duration)) { 130 | m.onInactive(cc) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /core/observe.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "github.com/plgd-dev/go-coap/v3/mux" 6 | "github.com/yplam/lwm2m/node" 7 | ) 8 | 9 | type ObserveFunc func(d *Device, p node.Path, notify []node.Node) 10 | type ObserveObjectFunc func(d *Device, p node.Path, notify *node.Object) 11 | type ObserveResourceFunc func(d *Device, p node.Path, notify *node.Resource) 12 | 13 | type Observation struct { 14 | o mux.Observation 15 | cb ObserveFunc 16 | } 17 | 18 | func wrapObserveResourceFunc(f ObserveResourceFunc) ObserveFunc { 19 | return func(d *Device, p node.Path, notify []node.Node) { 20 | if data, err := node.GetResourceByPath(notify, p); err == nil { 21 | f(d, p, data) 22 | } 23 | } 24 | } 25 | 26 | func wrapObserveObjectFunc(f ObserveObjectFunc) ObserveFunc { 27 | return func(d *Device, p node.Path, notify []node.Node) { 28 | if data, err := node.GetObjectByPath(notify, p); err == nil { 29 | f(d, p, data) 30 | } else { 31 | fmt.Printf("obj err %v\n", err) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /encoding/corelink_test.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestCoreLinksFromString(t *testing.T) { 9 | var links []*CoreLink 10 | 11 | links, err := CoreLinksFromString(";rt=\"oma.github.com/yplam/lwm2m\";" + 12 | "ct=11543,,,,,,,," + 13 | ",,,") 14 | assert.Nil(t, err) 15 | assert.Equal(t, 12, len(links)) 16 | } 17 | -------------------------------------------------------------------------------- /encoding/encoding.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrInvalidLength = errors.New("invalid data length") 7 | ErrNotEnoughData = errors.New("not enough data") 8 | ErrNotBoolean = errors.New("not a boolean representation") 9 | ErrUnknownType = errors.New("unknown type") 10 | ) 11 | 12 | type Valuer interface { 13 | StringVal() string 14 | Integer() (val int64, err error) 15 | Float() (float64, error) 16 | Boolean() (val bool, err error) 17 | Opaque() []byte 18 | Time() (int64, error) 19 | ObjectLink() (uint16, uint16, error) 20 | Raw() []byte 21 | } 22 | -------------------------------------------------------------------------------- /encoding/opaque.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "math" 7 | ) 8 | 9 | // Opaque value should be used for binary resources like firmware image 10 | // although define it for every type 11 | // with same rules as Value field in TLV 12 | 13 | type OpaqueValue struct { 14 | Value []byte 15 | } 16 | 17 | func (o *OpaqueValue) StringVal() string { 18 | return string(o.Value) 19 | } 20 | 21 | func (o *OpaqueValue) Integer() (val int64, err error) { 22 | buff := bytes.NewBuffer(o.Value) 23 | if len(o.Value) == 1 { 24 | var i1 int8 25 | err = binary.Read(buff, binary.BigEndian, &i1) 26 | val = int64(i1) 27 | } else if len(o.Value) == 2 { 28 | var i2 int16 29 | err = binary.Read(buff, binary.BigEndian, &i2) 30 | val = int64(i2) 31 | } else if len(o.Value) == 4 { 32 | var i4 int32 33 | err = binary.Read(buff, binary.BigEndian, &i4) 34 | val = int64(i4) 35 | } else if len(o.Value) == 8 { 36 | var i8 int64 37 | err = binary.Read(buff, binary.BigEndian, &i8) 38 | val = i8 39 | } else { 40 | err = ErrInvalidLength 41 | } 42 | return 43 | } 44 | 45 | func (o *OpaqueValue) Float() (float64, error) { 46 | if len(o.Value) == 4 { 47 | return float64(math.Float32frombits(binary.BigEndian.Uint32(o.Value))), nil 48 | } else if len(o.Value) == 8 { 49 | return math.Float64frombits(binary.BigEndian.Uint64(o.Value)), nil 50 | } else { 51 | return 0, ErrInvalidLength 52 | } 53 | } 54 | 55 | func (o *OpaqueValue) Boolean() (val bool, err error) { 56 | // Represented as the ASCII value 0 or 1 57 | if bytes.Equal([]byte{0x01}, o.Value) { 58 | return true, nil 59 | } else if bytes.Equal([]byte{0x00}, o.Value) { 60 | return false, nil 61 | } 62 | return false, ErrNotBoolean 63 | } 64 | 65 | func (o *OpaqueValue) Opaque() []byte { 66 | return o.Value 67 | } 68 | 69 | func (o *OpaqueValue) Time() (int64, error) { 70 | if len(o.Value) == 4 { 71 | return int64(binary.BigEndian.Uint32(o.Value)), nil 72 | } else if len(o.Value) == 8 { 73 | return int64(binary.BigEndian.Uint64(o.Value)), nil 74 | } else { 75 | return 0, ErrInvalidLength 76 | } 77 | } 78 | 79 | func (o *OpaqueValue) ObjectLink() (uint16, uint16, error) { 80 | if len(o.Value) != 4 { 81 | return 0, 0, ErrInvalidLength 82 | } 83 | _ = o.Value[3] 84 | return uint16(o.Value[0])*256 + uint16(o.Value[1]), 85 | uint16(o.Value[2])*256 + uint16(o.Value[3]), nil 86 | } 87 | 88 | func (o *OpaqueValue) Raw() []byte { 89 | return o.Value 90 | } 91 | 92 | func NewOpaqueValue(v any) (*OpaqueValue, error) { 93 | var val any 94 | switch v.(type) { 95 | case int: 96 | val = int64(v.(int)) 97 | case string: 98 | val = []byte(v.(string)) 99 | default: 100 | val = v 101 | } 102 | buf := new(bytes.Buffer) 103 | err := binary.Write(buf, binary.BigEndian, val) 104 | if err != nil { 105 | return nil, err 106 | } 107 | return &OpaqueValue{Value: buf.Bytes()}, nil 108 | } 109 | -------------------------------------------------------------------------------- /encoding/opaque_test.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestOpaque(t *testing.T) { 9 | { // int -> int64 10 | val, err := NewOpaqueValue(42) 11 | assert.Nil(t, err) 12 | assert.Equal(t, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a}, val.Raw()) 13 | } 14 | { // int64 15 | val, err := NewOpaqueValue(int64(42)) 16 | assert.Nil(t, err) 17 | assert.Equal(t, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a}, val.Raw()) 18 | } 19 | { // int32 20 | val, err := NewOpaqueValue(int32(42)) 21 | assert.Nil(t, err) 22 | assert.Equal(t, []byte{0x00, 0x00, 0x00, 0x2a}, val.Raw()) 23 | } 24 | { // int16 25 | val, err := NewOpaqueValue(int16(42)) 26 | assert.Nil(t, err) 27 | assert.Equal(t, []byte{0x00, 0x2a}, val.Raw()) 28 | } 29 | { // float #1 30 | val, err := NewOpaqueValue(42.0) 31 | assert.Nil(t, err) 32 | assert.Equal(t, []byte{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, val.Raw()) 33 | } 34 | { // float #2 35 | val, err := NewOpaqueValue(3.14151617181920) 36 | assert.Nil(t, err) 37 | assert.Equal(t, []byte{0x40, 0x09, 0x21, 0xd3, 0x3b, 0xe, 0x8c, 0x74}, val.Raw()) 38 | } 39 | { // string 40 | val, err := NewOpaqueValue("3.14151617181920") 41 | assert.Nil(t, err) 42 | assert.Equal(t, []byte("3.14151617181920"), val.Raw()) 43 | } 44 | { // bool 45 | val, err := NewOpaqueValue(true) 46 | assert.Nil(t, err) 47 | assert.Equal(t, []byte{0x01}, val.Raw()) 48 | val, err = NewOpaqueValue(false) 49 | assert.Equal(t, nil, err) 50 | assert.Equal(t, []byte{0x00}, val.Raw()) 51 | } 52 | { // opaque 53 | val, err := NewOpaqueValue([]byte{0x01, 0x02, 0x03, 0x04}) 54 | assert.Nil(t, err) 55 | assert.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, val.Raw()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /encoding/textplain.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type PlainTextValue struct { 12 | Value []byte 13 | } 14 | 15 | func (p *PlainTextValue) StringVal() string { 16 | return string(p.Value) 17 | } 18 | 19 | func (p *PlainTextValue) Integer() (val int64, err error) { 20 | return strconv.ParseInt(string(p.Value), 10, 64) 21 | } 22 | 23 | func (p *PlainTextValue) Float() (float64, error) { 24 | return strconv.ParseFloat(string(p.Value), 64) 25 | } 26 | 27 | func (p *PlainTextValue) Boolean() (val bool, err error) { 28 | // Represented as the ASCII value 0 or 1 29 | if string(p.Value) == "1" { 30 | return true, nil 31 | } else if string(p.Value) == "0" { 32 | return false, nil 33 | } 34 | return false, ErrNotBoolean 35 | } 36 | 37 | func (p *PlainTextValue) Opaque() []byte { 38 | // Represented as a Base64 encoding of binary bytes 39 | opaque, err := base64.StdEncoding.DecodeString(string(p.Value)) 40 | if err != nil { 41 | return nil 42 | } 43 | return opaque 44 | } 45 | 46 | func (p *PlainTextValue) Time() (int64, error) { 47 | // same as integer 48 | return p.Integer() 49 | } 50 | 51 | func (p *PlainTextValue) ObjectLink() (uint16, uint16, error) { 52 | splitted := strings.Split(string(p.Value), ":") 53 | if len(splitted) != 2 { 54 | return 0, 0, errors.New("wrong ObjLink text representation") 55 | } 56 | od, err := strconv.ParseUint(splitted[0], 10, 16) 57 | if err != nil { 58 | return 0, 0, err 59 | } 60 | oid, err := strconv.ParseUint(splitted[1], 10, 16) 61 | if err != nil { 62 | return 0, 0, err 63 | } 64 | return uint16(od), uint16(oid), nil 65 | } 66 | 67 | func (p *PlainTextValue) Raw() []byte { 68 | return p.Value 69 | } 70 | 71 | func NewPlainTextRaw(data []byte) *PlainTextValue { 72 | return &PlainTextValue{Value: data} 73 | } 74 | 75 | func (p *PlainTextValue) fromInteger(val int64) error { 76 | str := fmt.Sprintf("%d", val) 77 | p.Value = []byte(str) 78 | return nil 79 | } 80 | func (p *PlainTextValue) fromFloat(val float64) error { 81 | str := fmt.Sprintf("%v", val) 82 | p.Value = []byte(str) 83 | return nil 84 | } 85 | func (p *PlainTextValue) fromBool(val bool) error { 86 | str := "0" 87 | if val { 88 | str = "1" 89 | } 90 | p.Value = []byte(str) 91 | return nil 92 | } 93 | func (p *PlainTextValue) fromTime(val int64) error { 94 | return p.fromInteger(val) 95 | } 96 | func (p *PlainTextValue) fromString(val string) error { 97 | p.Value = []byte(val) 98 | return nil 99 | } 100 | func (p *PlainTextValue) fromOpaque(val []byte) error { 101 | raw := base64.StdEncoding.EncodeToString(val) 102 | p.Value = []byte(raw) 103 | return nil 104 | } 105 | func NewPlainTextValue(val any) (*PlainTextValue, error) { 106 | var pt PlainTextValue 107 | var err error 108 | switch val.(type) { 109 | case int: 110 | err = pt.fromInteger(int64(val.(int))) 111 | case int8: 112 | err = pt.fromInteger(int64(val.(int8))) 113 | case int16: 114 | err = pt.fromInteger(int64(val.(int16))) 115 | case int32: 116 | err = pt.fromInteger(int64(val.(int32))) 117 | case int64: 118 | err = pt.fromInteger(val.(int64)) 119 | case uint: 120 | err = pt.fromInteger(int64(val.(uint))) 121 | case uint8: 122 | err = pt.fromInteger(int64(val.(uint8))) 123 | case uint16: 124 | err = pt.fromInteger(int64(val.(uint16))) 125 | case uint32: 126 | err = pt.fromInteger(int64(val.(uint32))) 127 | case float32: 128 | err = pt.fromFloat(float64(val.(float32))) 129 | case float64: 130 | err = pt.fromFloat(val.(float64)) 131 | case string: 132 | err = pt.fromString(val.(string)) 133 | case []byte: 134 | err = pt.fromOpaque(val.([]byte)) 135 | case bool: 136 | err = pt.fromBool(val.(bool)) 137 | default: 138 | err = ErrUnknownType 139 | } 140 | if err != nil { 141 | return nil, err 142 | } 143 | return &pt, err 144 | } 145 | -------------------------------------------------------------------------------- /encoding/textplain_test.go: -------------------------------------------------------------------------------- 1 | package encoding 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestPlainText(t *testing.T) { 9 | { // int 10 | val, err := NewPlainTextValue(42) 11 | assert.Nil(t, err) 12 | assert.Equal(t, []byte("42"), val.Raw()) 13 | } 14 | { // float #1 15 | val, err := NewPlainTextValue(42.21) 16 | assert.Nil(t, err) 17 | assert.Equal(t, []byte("42.21"), val.Raw()) 18 | } 19 | { // float #2 20 | val, err := NewPlainTextValue(3.14151617181920) 21 | assert.Nil(t, err) 22 | assert.Equal(t, []byte("3.1415161718192"), val.Raw()) 23 | } 24 | { // string 25 | val, err := NewPlainTextValue("3.14151617181920") 26 | assert.Nil(t, err) 27 | assert.Equal(t, []byte("3.14151617181920"), val.Raw()) 28 | } 29 | { // bool 30 | val, err := NewPlainTextValue(true) 31 | assert.Nil(t, err) 32 | assert.Equal(t, []byte("1"), val.Raw()) 33 | val, err = NewPlainTextValue(false) 34 | assert.Equal(t, nil, err) 35 | assert.Equal(t, []byte("0"), val.Raw()) 36 | } 37 | { // opaque 38 | val, err := NewPlainTextValue([]byte{0x01, 0x02, 0x03, 0x04}) 39 | assert.Nil(t, err) 40 | assert.Equal(t, []byte("AQIDBA=="), val.Raw()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/content-format/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/plgd-dev/go-coap/v3/message" 7 | "github.com/yplam/lwm2m/core" 8 | "github.com/yplam/lwm2m/encoding" 9 | "github.com/yplam/lwm2m/node" 10 | "github.com/yplam/lwm2m/registration" 11 | "github.com/yplam/lwm2m/server" 12 | "log" 13 | "time" 14 | ) 15 | 16 | func onDeviceStateChange(e core.DeviceEvent, m core.Manager) { 17 | device := e.Device 18 | switch e.EventType { 19 | case core.DevicePostRegister: 20 | device.SetMediaTypes(message.TextPlain, message.TextPlain) 21 | if device.HasObjectWithInstance(1) { 22 | go func() { 23 | ctx := context.Background() 24 | op, _ := node.NewPathFromString("/1") 25 | oip, _ := node.NewPathFromString("/1/0") 26 | rp, _ := node.NewPathFromString("/1/0/1") 27 | if n, err := device.Discover(ctx, op); err == nil { 28 | fmt.Printf("links %v\n", n) 29 | } 30 | 31 | if v, err := device.Read(ctx, rp); err == nil { 32 | fmt.Printf("Read: %v\n", v) 33 | } 34 | time.Sleep(time.Second * 1) 35 | if v, err := device.ReadResource(ctx, rp); err == nil { 36 | fmt.Printf("Resource: %v\n", v) 37 | } 38 | time.Sleep(time.Second * 1) 39 | if v, err := device.ReadObject(ctx, op); err == nil { 40 | fmt.Printf("Object: %v\n", v) 41 | } 42 | time.Sleep(time.Second * 1) 43 | value := encoding.NewTlv(encoding.TlvSingleResource, 1, uint16(123)) 44 | if value != nil { 45 | if res, err := node.NewSingleResource(rp, value); err == nil { 46 | if err = device.WriteResource(ctx, rp, res); err == nil { 47 | fmt.Printf("Write resource ok\n") 48 | } else { 49 | fmt.Println("error writing resource: ", err) 50 | } 51 | } else { 52 | fmt.Println("error creating resource", err) 53 | } 54 | } 55 | time.Sleep(time.Second * 1) 56 | if v, err := device.ReadResource(ctx, rp); err == nil { 57 | fmt.Printf("Resource: %v\n", v) 58 | } 59 | time.Sleep(time.Second * 1) 60 | value = encoding.NewTlv(encoding.TlvSingleResource, 5850, true) 61 | if value != nil { 62 | if res, err := node.NewSingleResource(rp, value); err == nil { 63 | obi := node.NewObjectInstance(0) 64 | obi.Resources[5850] = res 65 | if err = device.WriteObjectInstance(ctx, oip, obi); err == nil { 66 | fmt.Printf("Write object instance ok\n") 67 | } 68 | } 69 | 70 | } 71 | time.Sleep(time.Second * 1) 72 | if v, err := device.ReadResource(ctx, rp); err == nil { 73 | fmt.Printf("Resource: %v\n", v) 74 | } 75 | }() 76 | 77 | } 78 | default: 79 | 80 | } 81 | } 82 | 83 | func main() { 84 | r := server.DefaultRouter() 85 | manager := core.DefaultManager() 86 | manager.OnDeviceStateChange(onDeviceStateChange) 87 | registration.EnableHandler(r, manager) 88 | 89 | err := server.ListenAndServe(r, 90 | server.EnableUDPListener("udp", ":5683")) 91 | if err != nil { 92 | log.Printf("Serve lwm2m with err: %v\n", err) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /examples/dtls/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/yplam/lwm2m/core" 5 | "github.com/yplam/lwm2m/registration" 6 | "github.com/yplam/lwm2m/server" 7 | "log" 8 | ) 9 | 10 | func PSKFromIdentity(hint []byte) ([]byte, error) { 11 | return []byte{ 12 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 13 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 14 | }, nil 15 | } 16 | 17 | func main() { 18 | r := server.DefaultRouter() 19 | deviceManager := core.DefaultManager() 20 | registration.EnableHandler(r, deviceManager) 21 | err := server.ListenAndServe(r, 22 | server.EnableUDPListener("udp", ":5683"), 23 | server.EnableDTLSListener("udp", ":5684", PSKFromIdentity), 24 | ) 25 | if err != nil { 26 | log.Printf("serve lwm2m with err: %v", err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/readwrite/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/yplam/lwm2m/core" 7 | "github.com/yplam/lwm2m/encoding" 8 | "github.com/yplam/lwm2m/node" 9 | "github.com/yplam/lwm2m/registration" 10 | "github.com/yplam/lwm2m/server" 11 | "log" 12 | "time" 13 | ) 14 | 15 | func onDeviceStateChange(e core.DeviceEvent, m core.Manager) { 16 | device := e.Device 17 | switch e.EventType { 18 | case core.DevicePostRegister: 19 | if device.HasObjectWithInstance(3347) { 20 | go func() { 21 | ctx := context.Background() 22 | op, _ := node.NewPathFromString("/3311") 23 | oip, _ := node.NewPathFromString("/3311/0") 24 | rp, _ := node.NewPathFromString("/3311/0/5850") 25 | if n, err := device.Discover(ctx, op); err == nil { 26 | fmt.Printf("links %v\n", n) 27 | } 28 | 29 | if v, err := device.Read(ctx, rp); err == nil { 30 | fmt.Printf("Read: %v\n", v) 31 | } 32 | time.Sleep(time.Second * 5) 33 | if v, err := device.ReadResource(ctx, rp); err == nil { 34 | fmt.Printf("Resource: %v\n", v) 35 | } 36 | time.Sleep(time.Second * 5) 37 | if v, err := device.ReadObject(ctx, op); err == nil { 38 | fmt.Printf("Object: %v\n", v) 39 | } 40 | time.Sleep(time.Second * 5) 41 | value := encoding.NewTlv(encoding.TlvSingleResource, 5850, false) 42 | if value != nil { 43 | if res, err := node.NewSingleResource(rp, value); err == nil { 44 | if err = device.WriteResource(ctx, rp, res); err == nil { 45 | fmt.Printf("Write resource ok\n") 46 | } 47 | } 48 | } 49 | time.Sleep(time.Second * 5) 50 | if v, err := device.ReadResource(ctx, rp); err == nil { 51 | fmt.Printf("Resource: %v\n", v) 52 | } 53 | time.Sleep(time.Second * 5) 54 | value = encoding.NewTlv(encoding.TlvSingleResource, 5850, true) 55 | if value != nil { 56 | if res, err := node.NewSingleResource(rp, value); err == nil { 57 | obi := node.NewObjectInstance(0) 58 | obi.Resources[5850] = res 59 | if err = device.WriteObjectInstance(ctx, oip, obi); err == nil { 60 | fmt.Printf("Write object instance ok\n") 61 | } 62 | } 63 | 64 | } 65 | time.Sleep(time.Second * 5) 66 | if v, err := device.ReadResource(ctx, rp); err == nil { 67 | fmt.Printf("Resource: %v\n", v) 68 | } 69 | }() 70 | 71 | } 72 | default: 73 | 74 | } 75 | } 76 | 77 | func main() { 78 | r := server.DefaultRouter() 79 | manager := core.DefaultManager() 80 | manager.OnDeviceStateChange(onDeviceStateChange) 81 | registration.EnableHandler(r, manager) 82 | 83 | err := server.ListenAndServe(r, 84 | server.EnableUDPListener("udp", ":5683")) 85 | if err != nil { 86 | log.Printf("Serve lwm2m with err: %v\n", err) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /examples/simple/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/yplam/lwm2m/core" 5 | "github.com/yplam/lwm2m/registration" 6 | "github.com/yplam/lwm2m/server" 7 | "log" 8 | ) 9 | 10 | func main() { 11 | r := server.DefaultRouter() 12 | deviceManager := core.DefaultManager() 13 | registration.EnableHandler(r, deviceManager) 14 | err := server.ListenAndServe(r, 15 | server.EnableUDPListener("udp", ":5683"), 16 | ) 17 | if err != nil { 18 | log.Printf("serve lwm2m with err: %v", err) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yplam/lwm2m 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/pion/dtls/v2 v2.2.6 7 | github.com/pion/logging v0.2.2 8 | github.com/plgd-dev/go-coap/v3 v3.1.2 9 | github.com/sirupsen/logrus v1.9.0 10 | github.com/stretchr/testify v1.8.2 11 | golang.org/x/sync v0.2.0 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/dsnet/golib/memfile v1.0.0 // indirect 17 | github.com/hashicorp/errwrap v1.1.0 // indirect 18 | github.com/hashicorp/go-multierror v1.1.1 // indirect 19 | github.com/pion/transport/v2 v2.2.0 // indirect 20 | github.com/pion/udp/v2 v2.0.1 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | go.uber.org/atomic v1.10.0 // indirect 23 | golang.org/x/crypto v0.8.0 // indirect 24 | golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect 25 | golang.org/x/net v0.10.0 // indirect 26 | golang.org/x/sys v0.8.0 // indirect 27 | gopkg.in/yaml.v3 v3.0.1 // indirect 28 | ) 29 | -------------------------------------------------------------------------------- /node/definition/10241.xml: -------------------------------------------------------------------------------- 1 |  31 | 32 | 33 | 34 | HostDeviceInfo 35 | 36 | 10241 37 | urn:oma:lwm2m:x:10241 38 | 1.0 39 | 1.0 40 | Multiple 41 | Optional 42 | 43 | Host Device Manufacturer 44 | R 45 | Multiple 46 | Mandatory 47 | String 48 | 49 | 50 | 51 | 52 | Host Device Model Number 53 | R 54 | Multiple 55 | Mandatory 56 | String 57 | 58 | 59 | 60 | 61 | Host Device Unique ID 62 | R 63 | Multiple 64 | Mandatory 65 | String 66 | 67 | 68 | 69 | 70 | Host Device Software Version 71 | R 72 | Multiple 73 | Mandatory 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /node/definition/10248.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | Connected UE Measurements 37 | This LWM2M Object provides a range of measurements of connected UEs and provides an Object link to the Connected UE report. 38 | 10248 39 | urn:oma:lwm2m:x:10248 40 | 1.0 41 | 1.0 42 | Single 43 | Optional 44 | 45 | 46 | Number of Connected Users 47 | R 48 | Single 49 | Mandatory 50 | Integer 51 | 0..255 52 | 53 | The number of different UEs currently connected to the eNB (i.e. in RRC_CONNECTED state). 54 | 55 | 56 | Cumulative Number of Unique Users 57 | R 58 | Single 59 | Mandatory 60 | Integer 61 | 0..65535 62 | 63 | The number of different UEs that have connected to the eNB over the immediately preceding period specified by the "Cumulative Measurement Window" field. 64 | 65 | 66 | Connected UE Report 67 | R 68 | Multiple 69 | Mandatory 70 | Objlnk 71 | 72 | 73 | Provides an Object link to the Connected UE Report which provides a range of information related to the connected UEs. 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /node/definition/10250.xml: -------------------------------------------------------------------------------- 1 |  31 | 32 | 33 | 34 | App Data Container 35 | 36 | 10250 37 | urn:oma:lwm2m:x:10250 38 | 1.0 39 | 1.0 40 | Single 41 | Optional 42 | 43 | 44 | UL data 45 | R 46 | Single 47 | Mandatory 48 | Opaque 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | DL data 57 | W 58 | Single 59 | Mandatory 60 | Opaque 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /node/definition/10253.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | Confidential Data 37 | 38 | 10253 39 | urn:oma:lwm2m:x:10253 40 | 1.0 41 | 1.0 42 | Single 43 | Optional 44 | 45 | 46 | Public Key 47 | RW 48 | Single 49 | Mandatory 50 | Opaque 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Application Data 59 | R 60 | Single 61 | Mandatory 62 | Opaque 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /node/definition/10286.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | App Fota Container 13 | 14 | 10286 15 | urn:oma:lwm2m:x:10286 16 | 1.0 17 | 1.0 18 | Single 19 | Optional 20 | 21 | 22 | UL data 23 | R 24 | Single 25 | Mandatory 26 | Opaque 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | DL data 35 | W 36 | Single 37 | Mandatory 38 | Opaque 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /node/definition/10322.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | CCU PM 36 | 37 | 10322 38 | urn:oma:lwm2m:x:10322 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | CPU Usage 46 | R 47 | Single 48 | Optional 49 | Integer 50 | 0..100 51 | % 52 | 53 | 54 | 55 | Max CPU Usage 56 | R 57 | Single 58 | Optional 59 | Integer 60 | 0..100 61 | % 62 | 63 | 64 | 65 | Memory Usage 66 | R 67 | Single 68 | Optional 69 | Integer 70 | 0..100 71 | % 72 | 73 | 74 | 75 | Storage Usage 76 | R 77 | Single 78 | Optional 79 | Integer 80 | 0..100 81 | % 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /node/definition/10323.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | CCU Control 36 | 37 | 10323 38 | urn:oma:lwm2m:x:10323 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 57 | 58 | CCU Restart 59 | E 60 | Single 61 | Mandatory 62 | 63 | 64 | 65 | 66 | 67 | 68 | CCU Reset 69 | E 70 | Single 71 | Mandatory 72 | 73 | 74 | 75 | 76 | 77 | 78 | CCU Self-checking 79 | E 80 | Single 81 | Mandatory 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /node/definition/10324.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | ECU 36 | 37 | 10324 38 | urn:oma:lwm2m:x:10324 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | ECU ID 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | ECU FM Version 56 | R 57 | Single 58 | Mandatory 59 | String 60 | 61 | 62 | 63 | 64 | 65 | On time 66 | RW 67 | Single 68 | Mandatory 69 | Integer 70 | 71 | s 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /node/definition/10327.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Compressor 36 | 37 | 10327 38 | urn:oma:lwm2m:x:10327 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Compressor Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | Compressor Status 57 | R 58 | Single 59 | Mandatory 60 | Boolean 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /node/definition/10328.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | SCA PM 36 | 37 | 10328 38 | urn:oma:lwm2m:x:10328 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | SCA Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | SCA Current 59 | R 60 | Single 61 | Mandatory 62 | Float 63 | 64 | A 65 | 66 | 67 | 68 | SCA Temperate 69 | R 70 | Single 71 | Mandatory 72 | Float 73 | 74 | Cel 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /node/definition/10332.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Robot Selfcheck Info 36 | 37 | 10332 38 | urn:oma:lwm2m:x:10332 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Entity 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 4..63 51 | 52 | 53 | .]]> 54 | 55 | 56 | 57 | Status 58 | R 59 | Single 60 | Mandatory 61 | Integer 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /node/definition/10333.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | PM Threshold 36 | 37 | 10333 38 | urn:oma:lwm2m:x:10333 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Entity 46 | RW 47 | Multiple 48 | Mandatory 49 | String 50 | 4..63 51 | 52 | .]]> 53 | 54 | 55 | Performance Type 56 | RW 57 | Multiple 58 | Mandatory 59 | String 60 | 61 | 62 | , for example:/10322/2/4]]> 64 | 65 | 66 | High Threshold 67 | RW 68 | Multiple 69 | Mandatory 70 | Float 71 | 72 | 73 | 74 | 75 | 76 | Low Threshold 77 | RW 78 | Multiple 79 | Mandatory 80 | Float 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /node/definition/10335.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Event 36 | 37 | 10335 38 | urn:oma:lwm2m:x:10335 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Entity 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 4..63 51 | 52 | .]]> 53 | 54 | 55 | Event Type 56 | R 57 | Single 58 | Mandatory 59 | Integer 60 | 0..65535 61 | 62 | 63 | 64 | 65 | Time 66 | R 67 | Single 68 | Mandatory 69 | Time 70 | 71 | 72 | 73 | 74 | 75 | Sequence No 76 | R 77 | Single 78 | Mandatory 79 | Integer 80 | 0..2^63-1 81 | 82 | 83 | 84 | 85 | Additional Info 86 | R 87 | Single 88 | Optional 89 | String 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /node/definition/10336.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | MIC 36 | 37 | 10336 38 | urn:oma:lwm2m:x:10336 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Host Device Unique ID 46 | R 47 | Single 48 | Optional 49 | String 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | Host Device Manufacturer 59 | R 60 | Single 61 | Optional 62 | String 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Host Device Model Number 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /node/definition/10341.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | GPS 36 | 37 | 10341 38 | urn:oma:lwm2m:x:10341 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Host Device Unique ID 46 | R 47 | Single 48 | Optional 49 | String 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | Host Device Manufacturer 59 | R 60 | Single 61 | Optional 62 | String 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Host Device Model Number 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /node/definition/10342.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | IMU 36 | 37 | 10342 38 | urn:oma:lwm2m:x:10342 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Host Device Unique ID 46 | R 47 | Single 48 | Optional 49 | String 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | Host Device Manufacturer 59 | R 60 | Single 61 | Optional 62 | String 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Host Device Model Number 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /node/definition/10343.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | LiDAR 36 | 37 | 10343 38 | urn:oma:lwm2m:x:10343 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | LiDAR Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Host Device Unique ID 58 | R 59 | Single 60 | Optional 61 | String 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | Host Device Manufacturer 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Host Device Model Number 83 | R 84 | Single 85 | Optional 86 | String 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /node/definition/10344.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Arm 36 | 37 | 10344 38 | urn:oma:lwm2m:x:10344 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Arm Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Host Device Unique ID 58 | R 59 | Single 60 | Optional 61 | String 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | Host Device Manufacturer 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Host Device Model Number 83 | R 84 | Single 85 | Optional 86 | String 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /node/definition/10345.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Leg 36 | 37 | 10345 38 | urn:oma:lwm2m:x:10345 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Leg Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Host Device Unique ID 58 | R 59 | Single 60 | Optional 61 | String 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | Host Device Manufacturer 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Host Device Model Number 83 | R 84 | Single 85 | Optional 86 | String 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /node/definition/10346.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Servomotor 36 | 37 | 10346 38 | urn:oma:lwm2m:x:10346 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Servomotor Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Host Device Unique ID 58 | R 59 | Single 60 | Optional 61 | String 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | Host Device Manufacturer 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Host Device Model Number 83 | R 84 | Single 85 | Optional 86 | String 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /node/definition/10348.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Wheel 36 | 37 | 10348 38 | urn:oma:lwm2m:x:10348 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Wheel Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Host Device Unique ID 58 | R 59 | Single 60 | Optional 61 | String 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | Host Device Manufacturer 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Host Device Model Number 83 | R 84 | Single 85 | Optional 86 | String 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /node/definition/10349.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Chassis 36 | 37 | 10349 38 | urn:oma:lwm2m:x:10349 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Host Device Unique ID 46 | R 47 | Single 48 | Optional 49 | String 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | Host Device Manufacturer 59 | R 60 | Single 61 | Optional 62 | String 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Host Device Model Number 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /node/definition/10351.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Door 36 | 37 | 10351 38 | urn:oma:lwm2m:x:10351 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Door Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | Door Status 57 | R 58 | Single 59 | Mandatory 60 | Boolean 61 | 62 | 63 | 64 | 65 | 66 | 67 | Door Control 68 | E 69 | Single 70 | Mandatory 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /node/definition/10353.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Warning Light 36 | 37 | 10353 38 | urn:oma:lwm2m:x:10353 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Light Status 46 | R 47 | Single 48 | Mandatory 49 | Boolean 50 | 51 | 52 | 53 | 54 | 55 | Light Warning 56 | R 57 | Single 58 | Mandatory 59 | Boolean 60 | 61 | 62 | 63 | 64 | 65 | Light Control 66 | E 67 | Single 68 | Mandatory 69 | 70 | 71 | 72 | 73 | 74 | 75 | Light Warning Control 76 | E 77 | Single 78 | Mandatory 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /node/definition/10355.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | General Info 36 | 37 | 10355 38 | urn:oma:lwm2m:x:10355 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Robot General Info 46 | R 47 | Single 48 | Mandatory 49 | Objlnk 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | RCU General Info 58 | R 59 | Single 60 | Mandatory 61 | Objlnk 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | CCU General Info 70 | R 71 | Multiple 72 | Optional 73 | Objlnk 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | ECU General Info 82 | R 83 | Multiple 84 | Optional 85 | Objlnk 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /node/definition/10357.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | PM 36 | 37 | 10357 38 | urn:oma:lwm2m:x:10357 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Robot PM 46 | R 47 | Single 48 | Mandatory 49 | Objlnk 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | RCU PM 58 | R 59 | Single 60 | Mandatory 61 | Objlnk 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | CCU PM 70 | R 71 | Multiple 72 | Optional 73 | Objlnk 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | SCA PM 82 | R 83 | Multiple 84 | Optional 85 | Objlnk 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /node/definition/10358.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Fan PM 36 | 37 | 10358 38 | urn:oma:lwm2m:x:10358 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Fan Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Fan Speed 59 | R 60 | Single 61 | Optional 62 | Integer 63 | 64 | 1/min 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /node/definition/10359.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Lock 36 | 37 | 10359 38 | urn:oma:lwm2m:x:10359 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Lock Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | Lock Status 56 | R 57 | Single 58 | Optional 59 | Boolean 60 | 61 | 62 | 63 | 64 | 65 | 66 | Lock Control 67 | E 68 | Single 69 | Mandatory 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /node/definition/10360.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Ultrasonic Sensor 36 | 37 | 10360 38 | urn:oma:lwm2m:x:10360 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Status 59 | R 60 | Single 61 | Mandatory 62 | Integer 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /node/definition/10361.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Collision Sensor 36 | 37 | 10361 38 | urn:oma:lwm2m:x:10361 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Status 58 | R 59 | Single 60 | Mandatory 61 | Integer 62 | 63 | 64 | 65 | 66 | 67 | 68 | Collision Detection 69 | R 70 | Single 71 | Optional 72 | Boolean 73 | 74 | 75 | 77 | 78 | 79 | 80 | Collision Detection Control 81 | E 82 | Single 83 | Mandatory 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /node/definition/10362.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Drop Sensor 36 | 37 | 10362 38 | urn:oma:lwm2m:x:10362 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Status 58 | R 59 | Single 60 | Mandatory 61 | Integer 62 | 63 | 64 | 65 | 66 | 67 | Drop Detection 68 | R 69 | Single 70 | Optional 71 | Boolean 72 | 73 | 74 | 76 | 77 | 78 | 79 | Drop Detection Control 80 | E 81 | Single 82 | Mandatory 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /node/definition/10363.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Temperature Sensor 36 | 37 | 10363 38 | urn:oma:lwm2m:x:10363 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Status 46 | R 47 | Single 48 | Mandatory 49 | Integer 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /node/definition/10364.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Humidity Sensor 36 | 37 | 10364 38 | urn:oma:lwm2m:x:10364 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Status 46 | R 47 | Single 48 | Mandatory 49 | Integer 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /node/definition/10365.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Gas-Dust Sensor 36 | 37 | 10365 38 | urn:oma:lwm2m:x:10365 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Status 46 | R 47 | Single 48 | Mandatory 49 | Integer 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /node/definition/10366.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | Fan 36 | 37 | 10366 38 | urn:oma:lwm2m:x:10366 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Fan Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /node/definition/10368.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | SpringMotor 36 | 37 | 10368 38 | urn:oma:lwm2m:x:10368 39 | 1.0 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | SpringMotor Name 46 | R 47 | Single 48 | Mandatory 49 | String 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Host Device Unique ID 58 | R 59 | Single 60 | Optional 61 | String 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | Host Device Manufacturer 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Host Device Model Number 83 | R 84 | Single 85 | Optional 86 | String 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /node/definition/10369.xml: -------------------------------------------------------------------------------- 1 | 2 | 32 | 33 | 34 | 35 | MCU 36 | 37 | 10369 38 | urn:oma:lwm2m:x:10369 39 | 1.0 40 | 1.0 41 | Single 42 | Optional 43 | 44 | 45 | Host Device Unique ID 46 | R 47 | Single 48 | Optional 49 | String 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | Host Device Manufacturer 59 | R 60 | Single 61 | Optional 62 | String 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Host Device Model Number 71 | R 72 | Single 73 | Optional 74 | String 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /node/definition/2049.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | ActiveCmdhPolicy 37 | 38 | 2049 39 | urn:oma:lwm2m:ext:20491.0 40 | 1.0Single 41 | Optional 42 | 43 | ActiveLink 44 | RW 45 | Single 46 | Mandatory 47 | Objlnk 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /node/definition/2050.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | CmdhDefaults 37 | 38 | 2050 39 | urn:oma:lwm2m:ext:20501.0 40 | 1.0Multiple 41 | Optional 42 | 43 | DefaultEcRules 44 | RW 45 | Multiple 46 | Mandatory 47 | Objlnk 48 | 49 | 50 | 51 | 52 | 53 | DefaultEcParamRules 54 | RW 55 | Multiple 56 | Mandatory 57 | Objlnk 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /node/definition/2054.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | CmdhNetworkAccessRules 37 | 38 | 2054 39 | urn:oma:lwm2m:ext:20541.0 40 | 1.0Multiple 41 | Optional 42 | 43 | ApplicableEventCategories 44 | RW 45 | Multiple 46 | Mandatory 47 | Integer 48 | 49 | 50 | 51 | 52 | NetworkAccessRule 53 | RW 54 | Multiple 55 | Optional 56 | Objlnk 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /node/definition/2056.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | CmdhBuffer 37 | 38 | 2056 39 | urn:oma:lwm2m:ext:20561.0 40 | 1.0Multiple 41 | Optional 42 | 43 | ApplicableEventCategory 44 | RW 45 | Multiple 46 | Mandatory 47 | Integer 48 | 49 | 50 | 51 | 52 | MaxBufferSize 53 | RW 54 | Single 55 | Mandatory 56 | Integer 57 | 58 | B 59 | 60 | 61 | StoragePriority 62 | RW 63 | Single 64 | Mandatory 65 | Integer 66 | 1..10 67 | 68 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /node/definition/26.xml: -------------------------------------------------------------------------------- 1 | 2 | 58 | 59 | 60 | 61 | LwM2M Gateway Routing 62 | 63 | 26 64 | urn:oma:lwm2m:oma:26 65 | 1.2 66 | 1.0 67 | Multiple 68 | Optional 69 | 70 | 71 | LwM2M Object ID 72 | R 73 | Single 74 | Mandatory 75 | Unsigned Integer 76 | 77 | 78 | 79 | 80 | 81 | Mapping Info 82 | R 83 | Single 84 | Mandatory 85 | Corelnk 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /node/definition/3348.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | Multi-state Selector 37 | This IPSO object is used to represent the state of a Multi-state selector switch with a number of fixed positions. 38 | 39 | 3348 40 | urn:oma:lwm2m:ext:3348:1.1 41 | 1.0 42 | 1.1 43 | Multiple 44 | Optional 45 | 46 | 47 | Multi-state Input 48 | R 49 | Single 50 | Mandatory 51 | Integer 52 | 53 | 54 | The current state of a Multi-state input or selector. 55 | 56 | 57 | Application Type 58 | RW 59 | Single 60 | Optional 61 | String 62 | 63 | 64 | The application type of the sensor or actuator as a string depending on the use case. 65 | 66 | 67 | Timestamp 68 | R 69 | Single 70 | Optional 71 | Time 72 | 73 | 74 | The timestamp of when the measurement was performed. 75 | 76 | 77 | Fractional Timestamp 78 | R 79 | Single 80 | Optional 81 | Float 82 | 0..1 83 | s 84 | Fractional part of the timestamp when sub-second precision is used (e.g., 0.23 for 230 ms). 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /node/definition/3400.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | oA Physical Presence Sensor 37 | 38 | 3400 39 | urn:oma:lwm2m:ext:3400 40 | 1.1 41 | 1.0 42 | Multiple 43 | Optional 44 | 45 | 46 | ObjectVersion 47 | R 48 | Single 49 | Optional 50 | String 51 | 52 | 53 | 54 | 55 | 56 | Documentary Description 57 | RW 58 | Single 59 | Optional 60 | String 61 | 0..256 bytes 62 | 63 | 64 | 65 | 66 | Error Status 67 | R 68 | Single 69 | Mandatory 70 | Unsigned Integer 71 | 0..255 72 | 73 | 74 | 75 | 76 | Mounting Location 77 | RW 78 | Single 79 | Mandatory 80 | String 81 | 1..64 bytes 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /node/definition/3407.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 33 | 34 | 35 | 36 | Smoke Alarm 37 | 38 | 3407 39 | urn:oma:lwm2m:ext:3407 40 | 1.1 41 | 1.0 42 | Multiple 43 | Optional 44 | 45 | 46 | Battery Percentage 47 | R 48 | Single 49 | Optional 50 | Float 51 | 0.00..100.00 52 | 53 | 54 | 55 | 56 | RSSI 57 | R 58 | Single 59 | Optional 60 | Float 61 | 62 | dBW 63 | 64 | 65 | 66 | Smoke Sensor State 67 | R 68 | Single 69 | Mandatory 70 | Boolean 71 | 72 | 73 | 74 | 75 | 76 | Detected CO2 percentage 77 | R 78 | Single 79 | Optional 80 | Float 81 | 0.00..100.00 82 | /100 83 | 84 | 85 | 86 | Alarm loudness 87 | R 88 | Single 89 | Optional 90 | Float 91 | 92 | dB 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /node/definition/3408.xml: -------------------------------------------------------------------------------- 1 | 33 | 34 | 35 | Manhole Cover 36 | 37 | 3408 38 | urn:oma:lwm2m:ext:3408 39 | 1.1 40 | 1.0 41 | Multiple 42 | Optional 43 | 44 | 45 | Safety Angle Threshold 46 | R 47 | Single 48 | Mandatory 49 | Integer 50 | 0..180 51 | rad 52 | 53 | 54 | 55 | Manhole Cover Tilt 56 | R 57 | Single 58 | Mandatory 59 | Integer 60 | 0..180 61 | rad 62 | 63 | 64 | 65 | Cover Open Status 66 | R 67 | Single 68 | Mandatory 69 | Boolean 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /node/definition/3419.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 33 | 35 | 36 | Photocell 37 | A uCIFI photocell object is used for lighting control, particularly to identify when light should be switched ON/OFF. 38 | 3419 39 | urn:oma:lwm2m:ext:3419 40 | 1.0 41 | 1.0 42 | Multiple 43 | Optional 44 | 45 | 46 | ON lux level 47 | RW 48 | Single 49 | Optional 50 | Float 51 | 52 | lx 53 | Lux level below which the photocell switches its relay ON or sends a switch ON message on the network. 54 | 55 | 56 | OFF lux level 57 | RW 58 | Single 59 | Optional 60 | Float 61 | 62 | lx 63 | Lux level above which the photocell switches its relay OFF or sends a switch OFF message on the network. 64 | 65 | 66 | Photocell status 67 | R 68 | Single 69 | Optional 70 | Boolean 71 | 72 | 73 | Set to True if lux level is below ON lux level. Set to False if lux level is above OFF lux. 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /node/definition/3420.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 33 | 35 | 36 | LED color light 37 | The uCIFI LED color light enables to control light colors of a LED luminaire. 38 | 3420 39 | urn:oma:lwm2m:ext:3420 40 | 1.0 41 | 1.0 42 | Multiple 43 | Optional 44 | 45 | 46 | RGB value 47 | RW 48 | Single 49 | Mandatory 50 | String 51 | 52 | 53 | Text string according to the RBG hexadecimal format with # (e.g. #FF0000 for 100% red). 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /node/definition/3438.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 33 | 35 | 36 | Edge computing time condition 37 | The uCIFI edge computing time condition object defines the day/time (defined as a CRON rule) at which a particular script should be executed by a device. 38 | 3438 39 | urn:oma:lwm2m:ext:3438 40 | 1.0 41 | 1.0 42 | Multiple 43 | Optional 44 | 45 | 46 | Execution time/day 47 | RW 48 | Single 49 | Mandatory 50 | String 51 | 52 | 53 | Date/time at which the script should be executed, formatted as a CRON rule. 54 | 55 | 56 | Script identifier 57 | RW 58 | Multiple 59 | Mandatory 60 | String 61 | 62 | 63 | Object URN/Resource ID of the script to apply to the times/days defined in the execution time/day resource. 64 | 65 | 66 | Non supported time condition 67 | R 68 | Single 69 | Optional 70 | Boolean 71 | 72 | 73 | Set to True if the device can’t support the condition. 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /node/definition/3439.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32 | 33 | 35 | 36 | Edge computing generic condition 37 | The uCIFI edge computing generic condition object defines a vendor-specific condition (e.g. when a resource or an object is equal to a particular value) at which a particular script should be executed by the device. 38 | 3439 39 | urn:oma:lwm2m:ext:3439 40 | 1.0 41 | 1.0 42 | Multiple 43 | Optional 44 | 45 | 46 | Vendor condition 47 | RW 48 | Single 49 | Optional 50 | Opaque 51 | 52 | 53 | Condition described in a vendor specific way. 54 | 55 | 56 | Script identifier 57 | RW 58 | Multiple 59 | Mandatory 60 | String 61 | 62 | 63 | Object URN/Resource ID of the script to apply to the time/days defined in the vendor condition resource. 64 | 65 | 66 | Non supported time condition 67 | R 68 | Single 69 | Optional 70 | Boolean 71 | 72 | 73 | Set to True if the device can’t support the condition. 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /node/object.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type Object struct { 9 | Id uint16 10 | Instances map[uint16]*ObjectInstance 11 | } 12 | 13 | func (o *Object) ID() uint16 { 14 | return o.Id 15 | } 16 | 17 | func (o *Object) String() string { 18 | var b strings.Builder 19 | b.WriteString("Object {") 20 | b.WriteString(fmt.Sprintf("id: %v, ", o.Id)) 21 | b.WriteString("instance: [") 22 | for k, v := range o.Instances { 23 | b.WriteString(fmt.Sprintf("%v:", k)) 24 | b.WriteString(v.String()) 25 | } 26 | b.WriteString("]") 27 | b.WriteString("}") 28 | return b.String() 29 | } 30 | 31 | type ObjectInstance struct { 32 | Id uint16 33 | Resources map[uint16]*Resource 34 | } 35 | 36 | func (i *ObjectInstance) ID() uint16 { 37 | return i.Id 38 | } 39 | 40 | func (i *ObjectInstance) String() string { 41 | var b strings.Builder 42 | b.WriteString("OBI {") 43 | b.WriteString(fmt.Sprintf("id: %v, ", i.Id)) 44 | b.WriteString("Resources: [") 45 | for k, v := range i.Resources { 46 | b.WriteString(fmt.Sprintf("%v:", k)) 47 | b.WriteString(v.String()) 48 | } 49 | b.WriteString("]") 50 | b.WriteString("}") 51 | return b.String() 52 | } 53 | 54 | func (i *ObjectInstance) SetResource(id uint16, res *Resource) { 55 | i.Resources[id] = res 56 | } 57 | 58 | func NewObject(id uint16) *Object { 59 | return &Object{ 60 | Id: id, 61 | Instances: make(map[uint16]*ObjectInstance), 62 | } 63 | } 64 | 65 | func NewObjectInstance(id uint16) *ObjectInstance { 66 | return &ObjectInstance{ 67 | Id: id, 68 | Resources: make(map[uint16]*Resource), 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /node/object_test.go: -------------------------------------------------------------------------------- 1 | package node 2 | -------------------------------------------------------------------------------- /node/path_test.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestNewPathFromString(t *testing.T) { 9 | p, err := NewPathFromString("/") 10 | assert.Nil(t, err) 11 | assert.Equal(t, true, p.IsRoot()) 12 | 13 | p, err = NewPathFromString("/3/") 14 | assert.Nil(t, err) 15 | assert.Equal(t, true, p.IsObject()) 16 | v, err := p.ObjectId() 17 | assert.Nil(t, err) 18 | assert.Equal(t, uint16(3), v) 19 | p, err = NewPathFromString("3") 20 | assert.Nil(t, err) 21 | assert.Equal(t, true, p.IsObject()) 22 | assert.Equal(t, "/3", p.String()) 23 | 24 | p, err = NewPathFromString("/3/4") 25 | assert.Nil(t, err) 26 | assert.Equal(t, true, p.IsObjectInstance()) 27 | 28 | p, err = NewPathFromString("/3/4/5") 29 | assert.Nil(t, err) 30 | assert.Equal(t, true, p.IsResource()) 31 | 32 | p, err = NewPathFromString("/3/4/5/6") 33 | assert.Nil(t, err) 34 | assert.Equal(t, true, p.IsResourceInstance()) 35 | assert.Equal(t, "/3/4/5/6", p.String()) 36 | } 37 | -------------------------------------------------------------------------------- /node/resource.go: -------------------------------------------------------------------------------- 1 | package node 2 | 3 | import ( 4 | "fmt" 5 | "github.com/yplam/lwm2m/encoding" 6 | "strings" 7 | ) 8 | 9 | type ResourceInstance struct { 10 | id uint16 11 | resType ResourceType 12 | data encoding.Valuer 13 | path Path 14 | } 15 | 16 | func (r *ResourceInstance) Value() interface{} { 17 | switch r.resType { 18 | case R_NONE: 19 | return r.data 20 | case R_STRING: 21 | return r.data.StringVal() 22 | case R_INTEGER: 23 | if d, err := r.data.Integer(); err == nil { 24 | return d 25 | } 26 | case R_FLOAT: 27 | if d, err := r.data.Float(); err == nil { 28 | return d 29 | } 30 | case R_BOOLEAN: 31 | if d, err := r.data.Boolean(); err == nil { 32 | return d 33 | } 34 | case R_OPAQUE: 35 | return r.data.Opaque() 36 | case R_TIME: 37 | if d, err := r.data.Time(); err == nil { 38 | return d 39 | } 40 | case R_OBJLNK: 41 | if d0, d1, err := r.data.ObjectLink(); err == nil { 42 | return [2]uint16{d0, d1} 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | func (r *ResourceInstance) ID() uint16 { 49 | return r.id 50 | } 51 | 52 | func (r *ResourceInstance) String() string { 53 | return fmt.Sprintf("%v", r.Value()) 54 | } 55 | 56 | func (r *ResourceInstance) Data() encoding.Valuer { 57 | return r.data 58 | } 59 | 60 | func NewResourceInstance(p Path, data encoding.Valuer) (r *ResourceInstance, err error) { 61 | id, err := p.ResourceInstanceId() 62 | if err != nil { 63 | id = 0 64 | p.resourceInstanceId = 0 65 | } 66 | resType, err := GetRegistry().DetectResourceType(p) 67 | if err != nil { 68 | return 69 | } 70 | r = &ResourceInstance{ 71 | id: id, 72 | resType: resType, 73 | data: data, 74 | path: p, 75 | } 76 | return 77 | } 78 | 79 | type Resource struct { 80 | id uint16 81 | isMultiple bool 82 | instances map[uint16]*ResourceInstance 83 | path Path 84 | } 85 | 86 | func (r *Resource) SetInstance(val *ResourceInstance) error { 87 | if val.path.IsResourceInstance() { 88 | riid, _ := val.path.ResourceInstanceId() 89 | r.instances[riid] = val 90 | if riid > 0 { 91 | r.isMultiple = true 92 | } 93 | return nil 94 | } 95 | return ErrPathNotMatch 96 | } 97 | 98 | func (r *Resource) InstanceCount() int { 99 | return len(r.instances) 100 | } 101 | func (r *Resource) GetInstance(index uint16) (*ResourceInstance, error) { 102 | if ins, ok := r.instances[index]; ok { 103 | return ins, nil 104 | } 105 | return nil, ErrNotFound 106 | } 107 | 108 | func (r *Resource) ID() uint16 { 109 | return r.id 110 | } 111 | 112 | func (r *Resource) String() string { 113 | var b strings.Builder 114 | b.WriteString("Resource { ") 115 | b.WriteString(fmt.Sprintf("id: %v, ", r.id)) 116 | b.WriteString("instances: [ ") 117 | for k, v := range r.instances { 118 | b.WriteString(fmt.Sprintf("%v:", k)) 119 | b.WriteString(v.String()) 120 | } 121 | b.WriteString("] ") 122 | b.WriteString("}") 123 | return b.String() 124 | } 125 | 126 | func (r *Resource) Data() encoding.Valuer { 127 | if i, err := r.GetInstance(0); err == nil { 128 | return i.Data() 129 | } 130 | return nil 131 | } 132 | 133 | func NewResource(p Path, isMultiple bool) (r *Resource, err error) { 134 | resID, err := p.ResourceId() 135 | if err != nil { 136 | return 137 | } 138 | r = &Resource{ 139 | id: resID, 140 | isMultiple: isMultiple, 141 | instances: make(map[uint16]*ResourceInstance), 142 | path: p, 143 | } 144 | return 145 | } 146 | 147 | func NewSingleResource(p Path, val encoding.Valuer) (r *Resource, err error) { 148 | r, err = NewResource(p, false) 149 | if err != nil { 150 | return 151 | } 152 | p.SetResourceInstanceId(0) 153 | ri, err := NewResourceInstance(p, val) 154 | if err != nil { 155 | return 156 | } 157 | err = r.SetInstance(ri) 158 | return 159 | } 160 | -------------------------------------------------------------------------------- /registration/config.go: -------------------------------------------------------------------------------- 1 | package registration 2 | 3 | import "github.com/pion/logging" 4 | 5 | type config struct { 6 | logger logging.LeveledLogger 7 | } 8 | 9 | func newConfig() *config { 10 | return &config{ 11 | logger: nil, 12 | } 13 | } 14 | 15 | type Option func(cfg *config) 16 | 17 | func WithLogger(l logging.LeveledLogger) Option { 18 | return func(o *config) { 19 | o.logger = l 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/config.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/pion/dtls/v2" 5 | "github.com/pion/logging" 6 | ) 7 | 8 | type config struct { 9 | udpNetwork string 10 | udpAddr string 11 | tcpNetwork string 12 | tcpAddr string 13 | dtlsNetwork string 14 | dtlsAddr string 15 | pskCallback dtls.PSKCallback 16 | logger logging.LeveledLogger 17 | } 18 | 19 | func newServeConfig() *config { 20 | return &config{ 21 | udpNetwork: "", 22 | udpAddr: "", 23 | tcpNetwork: "", 24 | tcpAddr: "", 25 | dtlsNetwork: "", 26 | dtlsAddr: "", 27 | pskCallback: nil, 28 | logger: nil, 29 | } 30 | } 31 | 32 | type Option func(cfg *config) 33 | 34 | func WithLogger(l logging.LeveledLogger) Option { 35 | return func(o *config) { 36 | o.logger = l 37 | } 38 | } 39 | 40 | func EnableUDPListener(network, addr string) Option { 41 | return func(o *config) { 42 | o.udpNetwork = network 43 | o.udpAddr = addr 44 | } 45 | } 46 | 47 | func EnableTCPListener(network, addr string) Option { 48 | return func(o *config) { 49 | o.tcpNetwork = network 50 | o.tcpAddr = addr 51 | } 52 | } 53 | 54 | func EnableDTLSListener(network, addr string, cb dtls.PSKCallback) Option { 55 | return func(o *config) { 56 | o.dtlsNetwork = network 57 | o.dtlsAddr = addr 58 | o.pskCallback = cb 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /server/dtls.go: -------------------------------------------------------------------------------- 1 | package server 2 | -------------------------------------------------------------------------------- /server/udp.go: -------------------------------------------------------------------------------- 1 | package server 2 | --------------------------------------------------------------------------------