// The following code is a slightly modified version of code taken from the Paho MQTT library. // It is here until TinyGo can compile the "net" package from the standard library, at which time // it can be removed. /* * Copyright (c) 2013 IBM Corp. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Seth Hoenig * Allan Stockdill-Mander * Mike Robertson */ // Portions copyright © 2018 TIBCO Software Inc. package mqtt import ( "strings" "time" "github.com/eclipse/paho.mqtt.golang/packets" "tinygo.org/x/drivers/net" ) const ( disconnected uint32 = iota connecting reconnecting connected ) // Client is the interface definition for a Client as used by this // library, the interface is primarily to allow mocking tests. // // It is an MQTT v3.1.1 client for communicating // with an MQTT server using non-blocking methods that allow work // to be done in the background. // An application may connect to an MQTT server using: // A plain TCP socket // A secure SSL/TLS socket // A websocket // To enable ensured message delivery at Quality of Service (QoS) levels // described in the MQTT spec, a message persistence mechanism must be // used. This is done by providing a type which implements the Store // interface. For convenience, FileStore and MemoryStore are provided // implementations that should be sufficient for most use cases. More // information can be found in their respective documentation. // Numerous connection options may be specified by configuring a // and then supplying a ClientOptions type. type Client interface { // IsConnected returns a bool signifying whether // the client is connected or not. IsConnected() bool // IsConnectionOpen return a bool signifying wether the client has an active // connection to mqtt broker, i.e not in disconnected or reconnect mode IsConnectionOpen() bool // Connect will create a connection to the message broker, by default // it will attempt to connect at v3.1.1 and auto retry at v3.1 if that // fails Connect() Token // Disconnect will end the connection with the server, but not before waiting // the specified number of milliseconds to wait for existing work to be // completed. Disconnect(quiesce uint) // Publish will publish a message with the specified QoS and content // to the specified topic. // Returns a token to track delivery of the message to the broker Publish(topic string, qos byte, retained bool, payload interface{}) Token // Subscribe starts a new subscription. Provide a MessageHandler to be executed when // a message is published on the topic provided, or nil for the default handler Subscribe(topic string, qos byte, callback MessageHandler) Token // SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to // be executed when a message is published on one of the topics provided, or nil for the // default handler SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token // Unsubscribe will end the subscription from each of the topics provided. // Messages published to those topics from other clients will no longer be // received. Unsubscribe(topics ...string) Token // AddRoute allows you to add a handler for messages on a specific topic // without making a subscription. For example having a different handler // for parts of a wildcard subscription AddRoute(topic string, callback MessageHandler) // OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions // in use by the client. OptionsReader() ClientOptionsReader } // Token defines the interface for the tokens used to indicate when // actions have completed. type Token interface { Wait() bool WaitTimeout(time.Duration) bool Error() error } // MessageHandler is a callback type which can be set to be // executed upon the arrival of messages published to topics // to which the client is subscribed. type MessageHandler func(Client, Message) // Message defines the externals that a message implementation must support // these are received messages that are passed to the callbacks, not internal // messages type Message interface { Duplicate() bool Qos() byte Retained() bool Topic() string MessageID() uint16 Payload() []byte Ack() } type message struct { duplicate bool qos byte retained bool topic string messageID uint16 payload []byte ack func() } func (m *message) Duplicate() bool { return m.duplicate } func (m *message) Qos() byte { return m.qos } func (m *message) Retained() bool { return m.retained } func (m *message) Topic() string { return m.topic } func (m *message) MessageID() uint16 { return m.messageID } func (m *message) Payload() []byte { return m.payload } func (m *message) Ack() { return } func messageFromPublish(p *packets.PublishPacket, ack func()) Message { return &message{ duplicate: p.Dup, qos: p.Qos, retained: p.Retain, topic: p.TopicName, messageID: p.MessageID, payload: p.Payload, ack: ack, } } // ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized. type ClientOptionsReader struct { options *ClientOptions } // ClientOptions contains configurable options for an MQTT Client. type ClientOptions struct { Adaptor net.Adapter //Servers []*url.URL Servers string ClientID string Username string Password string //CredentialsProvider CredentialsProvider CleanSession bool Order bool WillEnabled bool WillTopic string WillPayload []byte WillQos byte WillRetained bool ProtocolVersion uint protocolVersionExplicit bool //TLSConfig *tls.Config KeepAlive int64 PingTimeout time.Duration ConnectTimeout time.Duration MaxReconnectInterval time.Duration AutoReconnect bool //Store Store //DefaultPublishHandler MessageHandler //OnConnect OnConnectHandler //OnConnectionLost ConnectionLostHandler WriteTimeout time.Duration MessageChannelDepth uint ResumeSubs bool //HTTPHeaders http.Header } // NewClientOptions returns a new ClientOptions struct. func NewClientOptions() *ClientOptions { return &ClientOptions{Adaptor: net.ActiveDevice, ProtocolVersion: 4} } // AddBroker adds a broker URI to the list of brokers to be used. The format should be // scheme://host:port // Where "scheme" is one of "tcp", "ssl", or "ws", "host" is the ip-address (or hostname) // and "port" is the port on which the broker is accepting connections. // // Default values for hostname is "127.0.0.1", for schema is "tcp://". // // An example broker URI would look like: tcp://foobar.com:1883 func (o *ClientOptions) AddBroker(server string) *ClientOptions { if len(server) > 0 && server[0] == ':' { server = "127.0.0.1" + server } if !strings.Contains(server, "://") { server = "tcp://" + server } o.Servers = server return o } // SetClientID will set the client id to be used by this client when // connecting to the MQTT broker. According to the MQTT v3.1 specification, // a client id mus be no longer than 23 characters. func (o *ClientOptions) SetClientID(id string) *ClientOptions { o.ClientID = id return o } // SetUsername will set the username to be used by this client when connecting // to the MQTT broker. Note: without the use of SSL/TLS, this information will // be sent in plaintext accross the wire. func (o *ClientOptions) SetUsername(u string) *ClientOptions { o.Username = u return o } // SetPassword will set the password to be used by this client when connecting // to the MQTT broker. Note: without the use of SSL/TLS, this information will // be sent in plaintext accross the wire. func (o *ClientOptions) SetPassword(p string) *ClientOptions { o.Password = p return o } // SetWill accepts a string will message to be set. When the client connects, // it will give this will message to the broker, which will then publish the // provided payload (the will) to any clients that are subscribed to the provided // topic. func (o *ClientOptions) SetWill(topic string, payload string, qos byte, retained bool) *ClientOptions { o.SetBinaryWill(topic, []byte(payload), qos, retained) return o } // SetBinaryWill accepts a []byte will message to be set. When the client connects, // it will give this will message to the broker, which will then publish the // provided payload (the will) to any clients that are subscribed to the provided // topic. func (o *ClientOptions) SetBinaryWill(topic string, payload []byte, qos byte, retained bool) *ClientOptions { o.WillEnabled = true o.WillTopic = topic o.WillPayload = payload o.WillQos = qos o.WillRetained = retained return o }