@wizzilab/wizzigate-api
v2.2.0
Published
Library implementing the Wizzilab gateway MQTT API.
Downloads
27
Readme
This library implements the Wizzilab gateway MQTT API. It relies on the mqtt.js and the [alp-lib][npm_alp_lib] libraries.
- API
- Gateway related classes
- GatewayAggregator related classes
- class GatewayAggregator
- class NamedGateway
- Common library types
- type HashMap
- class Event
- class State
- interface IMqttConfiguration
- interface IJsonRequestOptions
- class AppVersion
- type AsyncResult
- module GatewayError
- MQTT API specification
- Anomaly streams
- Examples
API ↑
Gateway related classes ↑
class Gateway ↑
Static methods
from_mqtt_client(mqtt_client, configuration)
: GatewayAggregator
mqtt_client
(mqtt.MqttClient
): MQTT client generated using the MQTT.js library.configuration
(IConfiguration
): Gateway configuration.
connect(mqtt_configuration, configuration)
: Gateway
mqtt_configuration
(IMqttConfiguration
): Mqtt configuration to connect to the gateway.configuration
(IConfiguration
): Gateway configuration.
Attributes
<<<<<<< HEAD
control
(Control
): Gateway distant control API.cup
(Cup
): Gateway upgrade manager API.modems
({<UID>
:Modem
}): Hash map containing the gateway's modems. This hash map is populated upon reception of packets from the gateway modems.status
:version
(State
<[
AppVersion
]
>]): version State of the gateway.full_version
(State
<[
AppVersion
]
>]): version State of the gateway.anomalies
(Event
<[
ErrorChain
]
>): aggregation of the anomalies caught by the gateway submodules.control
(Control
): Gateway distant control API.cup
(Cup
): Gateway upgrade manager API.modems
({<UID>
:Modem
}): Hash map containing the gateway's modems. This hash map is populated upon reception of packets from the gateway modems.status
:version
(Version
): Last received version of the gateway host.full_version
(FullVersion
): Last received version of the gateway modem.622dad3... [gateway] Add FullVersion class, and methods
Methods
add_all_modems_listener(callback)
Registers a callback that will be called once on each modem of the gateway (currently known and the ones discovered afterwards).
callback ((modem: Modem) => void)
: Callback to register.
remove_all_modems_listener(callback)
Unsubscribes a callback from add_all_modems_listener
.
version():
[Option
][npm_monads]<
AppVersion
>
Returs the version of the gateway if available.
Parameters
interface IConfiguration
Description of the configuration object to pass to the gateway constructor.
username (string)
: MQTT username used. Necessary to respect the MQTT ACL of the gateway (else your request might end up being ignored by the gateway).id_root (string)
: Optional. A token ID that will be inserted in all MQTT request channels ID used (to be able to track your request if you are watching them on a separate MQTT client).no_default_random_id_root (boolean)
: Optional. Prevents the lib from auto-adding a random token in the request IDs. Beware of the request id collisions.no_user_id_prefix (boolean)
: Optional. Prevents using theusername
in the request topic for special use cases (like emulating request from multiple users through access of the gateway via a gateway aggregator).mqtt_root_topic
: Optional. Defines the root topic where the gateway streams are. On the gateways' brokers, the root topic is"/"
(default configuration). If you forward the traffic of the gateway via a bridge to an external broker (and for some reason you prefer not to use the GatewayAggregator class), the root topic will be"/gw/<UID>/"
.
class Modem ↑
Class representing one of the gateway modems.
Remarks: if no listener has been set for the report
event, the library won't be parsing the
incoming data (reducing the parsing load). The library will still parse the gateway version reports
which are on separated MQTT topics.
<<<<<<< HEAD
Attributes
uid (string)
: UID of the modem (uppercase).version
(State
<[
AppVersion
]
>]): version State of the modem.reports
(Event
<[report, meta]
>): -report (any[])
: Parsed ALP packet. -meta
: -mqtt (IMqttPacketMeta)
: For internal use. -special (string)
: Optional. Set on special report such ashw_report
andsw_report
(modem and host version reports).raw_reports
(Event
<[payload, meta]
>): -report (Buffer)
: Mqtt payload. -meta (
IReportMeta
)
: Report metadatauid (string)
: UID of the modem (uppercase).version
(Version
): Last received version of the gateway modem.full_version
(FullVersion
): Last received version of the gateway modem.622dad3... [gateway] Add FullVersion class, and methods
Methods
request(request, options)
: AsyncResult
<
[AlpPacket[]
][npm_alp_lib]>
Sends an ALP request to the modem and returns the response.
request (Buffer)
: ALP request.options
:to (number)
: Request timeout. This corresponds the maximum time allowed between 2 response packet.id (string)
: Optional. ID to use to send this request.ignore_alp_error
: Optional. Don't treat ALP error statuses as request errors.
request_with_realtime_responses(request, options, callback)
: AsyncResult<void>
Low level API to send an ALP request to the modem. Sends the request and then calls the callback for each individual response packet received. The return promise is resolved upon reception of the ALP end of packet.
request (Buffer)
: ALP request.options
:to (number)
: Request timeout. This corresponds the maximum time allowed between 2 response packet.id (string)
: Optional. ID to use to send this request.
callback
: (pkt, meta) =>void
pkt (object)
: Alp packet object.meta
:sn (number)
: Response packet seqnum.error (boolean)
: Means this response was sent using the modem driver error stream (modem driver error).end (boolean)
: Means this is the last response packet for this request.mqtt (IMqttPacketMeta)
: For internal use.
class Control ↑
Methods
ping(options)
: AsyncResult
<[]>
Pings the control service of the gateway. Commonly used to test the MQTT link to the gateway.
options
(IJsonRequestOptions): Request options.
restart_dmons(options)
: AsyncResult
<[]>
Restarts the deamons of the gateway.
options
(IJsonRequestOptions): Request options.
restart_modems(options)
: AsyncResult
<[]>
Restarts all the gateway modems.
options
(IJsonRequestOptions): Request options.
reboot(options)
: AsyncResult
<[]>
Reboots the gateway.
options
(IJsonRequestOptions): Request options.
register(credentials, options)
: AsyncResult
<[]>
Registers the gateway to the server.
credentials
:username (string)
: Server account username.password (string)
: Server account password.serial (string)
: Gateway serial number.
options
(IJsonRequestOptions): Request options.
setup(config, options)
: AsyncResult
<[]>
Sets the configuration of the gateway. All of the configuration parts are tagged optional because this commands allows partial configuration to be set.
config
:system
(ISystemConfig
): Optional.mqtt
(IMqttConfig
): Optional.network
: Optional.ethernet
(IEthernetConfig
): Optional.wifi
(IWifiConfig
): Optional.gsm
(IGsmConfig
): Optional.dns
(IDnsConfig
): Optional.
options
(IJsonRequestOptions): Request options.
ISystemConfig
hostname (string)
: Optional. Hostname of the gateway (this will change the avahi name on the network, the gateway hostname and the web interface gateway name).web_credentials
: Optional. Credentials to loging on the web interface of the gateway.username (string)
password (string)
IMqttConfig
broker
: Optional. Configuration regarding the gateway MQTT broker.credentials
: Optional. Sets the credentials of a user on the gateway.username (string)
password (string)
bridge
: Optional. Configuration of the MQTT secondary bridge.address (string)
: Address of the broker targeted by the bridge.port (number)
: Optional. MQTT port of the broker (default: 1883).username (string)
: Optional.password (string)
: Optional.topic_prefix (string)
: Optional. Topic header used for the topic mapping on the distant broker.cafile (string)
: Optional. Broker certificate for SSL connections.
IEthernetConfig
- DHCP
type
="dhcp"
- Static IP configuration
type
="static"
ip_address (string)
subnetmask (string)
broadcast (string)
gateway (string)
IWifiConfig
Client with dhcp
type
="client"
ssid (string)
: SSID of the targeted wifi network.password (string)
: Password of the targeted wifi network.ip_config
:type
="dhcp"
Client with static IP configuration
type
="client"
ssid (string)
: SSID of the targeted wifi network.password (string)
: Password of the targeted wifi network.ip_config
:type
="static"
ip_address (string)
subnetmask (string)
broadcast (string)
gateway (string)
Access point
type
="access_point"
password (string)
: Password the clients will need to connect to the gateway wifi.enable_dhcp (boolean)
: Whether the gateway should act as a DHCP server for the clients.
IGsmConfig
apn (string)
: APN.pin (string)
: Optional. PIN code of the SIM card.username (string)
: Optional.password (string)
: Optional.
IDnsConfig
addresses (string[])
: List of DNS IP addresses. The current gateways only support up to 2 DNS.
class Cup ↑
Methods
clean(options)
: AsyncResult
<[]>
Cleans the CUP download directory.
options
(IJsonRequestOptions): Request options.
fetch(file, options)
: AsyncResult
<[]>
Downloads a file from an HTTP(S) URL to the gateway.
file
:name (string)
: Name of the file that will be saved.md5sum (string)
: Md5sum signature of the file.url (string)
: URL from which the file can be fetched.
options
(IJsonRequestOptions): Request options.
gateway_update(source, options)
: AsyncResult
<[]>
Updates the gateway host using the described file.
source
:name (string)
: Name of the file that will be used for the upgrade.md5sum (string)
: Md5sum signature of the file to use.full_system (boolean)
: Indicates whether this is a full system update (as opposed to a package update).
options
(IJsonRequestOptions): Request options.
devices_update(command, options)
: AsyncResult
<[]>
Updates a list of distant devices using the given configuration and the selected pre downloaded files. The update files transfer will target the devices matching the specified filters (using Dash7 queries).
command
: -gateway_modem_uid (string)
: Gateway modem to use. -xcls (number[])
: List of the listening access classes of the devices to reach. -filter
: (Even though each filter is optional, specifying them is highly recommended for safety reasons) -host
(IDeviceFilter
): Optional (recommended). Host version filter. -modem
(IDeviceFilter
): Optional (recommended). Modem version filter. -uids: string[]
: List of the target devices' UIDs in hexadecimal. -target_type ("host" | "modem")
: Target type of the code upgrade. -cup_version (number)
: Cup protocol version to use. - 1: (Deprecated) Uses the old CUP command file to switch the devices into CUP mode. Executes both the file transfer and execution phases in broadcast. - 2: (Current) Uses the new CUP broadcast file to switch the devices into CUP mode. Executes the file transfer in broadcast. The execution phase (sending the upgrade execution command) is executed for each device as a unicast procedure (targeting each UID specified inuids
). This is safer than the version 1 of the protocol. -files (Array<{name: string, md5sum: string}>)
: List of the CUP files to use (must already have been downloaded by the gateway via cup.fetch). -max_size (number)
: Max file size available to transfer the new firmware. This information is available on the device version file (file 2 for the modem | file 65 for the host). It should be identical for all the devices of the same firmware version, therefore allowing broadcast file transfers. -cfg_fid (number)
: Optional. Overrides used CUP configuration file id. -code_fid (number)
: Optional. Overrides used CUP code file id. -page_size (number)
: Optional. Specifies target page size. -query (string: hex)
: Optional. Overrides the procedure filter query with this raw ALP query.options
(IJsonRequestOptions): Request options.
devices_file_transfer(command, options)
: AsyncResult
<[]>
Transfer the selected files (pre uploaded on the gateway) to the target devices.
command
: -gateway_modem_uid (string)
: Gateway modem to use. -xcls (number[])
: List of the listening access classes of the devices to reach. -filter
: -host
(IDeviceFilter
): Optional. Host version filter -modem
(IDeviceFilter
): Optional. Modem version filter -uids: string[]
: List of the target devices' UIDs in hexadecimal. -target_type ("host" | "modem")
: Target type of the code upgrade. -files (Array<{name: string, md5sum: string}>)
: List of the CUP files to use (must already have been downloaded by the gateway via cup.fetch). -max_size (number)
: Max file size available to transfer the new firmware. This information is available on the device version file (file 2 for the modem | file 65 for the host). It should be identical for all the devices of the same firmware version, therefore allowing broadcast file transfers. -cfg_fid (number)
: Optional. Overrides used CUP configuration file id. -code_fid (number)
: Optional. Overrides used CUP code file id. -page_size (number)
: Optional. Specifies the target page size. -query (string: hex)
: Optional. Overrides the procedure filter query with this raw ALP query.options
(IJsonRequestOptions): Request options.
devices_action(command, options)
: AsyncResult
<[]>
Executes a list of actions on a list of devices.
command
:gateway_modem_uid (string)
: Gateway modem to use.xcls (number[])
: List of the listening access classes of the devices to reach.filter
:host
(IDeviceFilter
): Optional. Host version filter.modem
(IDeviceFilter
): Optional. Modem version filter.
uids: string[]
: List of the target devices' UIDs in hexadecimal.query (string: hex)
: Optional. Overrides the procedure filter query with this raw ALP query.cmds
(Array<ICupCommand>
): ICupCommand definition:cmd ("wr" | "wf")
: Command type. (write file / write flush)file_id (number)
file_offset (number)
data (string)
options
(IJsonRequestOptions): Request options.
Parameters
interface IDeviceFilter
type (string)
: [Device type][wiki_cup].hwv (string)
: [Hardware type][wiki_cup].fwv
:id (number)
major (number)
minor (number)
patch (number)
class Watchdog ↑
Attributes
processes
([HashMap
]<
State
<[
ProcessState
]>>)
: states of the gateway processes.new_processes
(Event
<[string]
>): returns the name of the newly detected processes.
Methods
add_all_processes_listener(callback)
Registers a callback to monitor the states of all the processes. The callback will be registered to
an internal Event
that will behave as if the callback had been registered to all each
processes state individually (receiving the initial state for example).
callback
:(name, state) => void
name (string)
: Process name.state (enum
ProcessState
)
: State of the process.
remove_all_processes_listener(callback)
Unsubscribes a callback from add_all_processes_listener
.
Parameters
enum ProcessState
Enumeration of the available cup protocols exported by the library as ProcessState
:
Unknown
Ready
Init
Dead
class Bridges ↑
Attributes
bridges (
HashMap
<
State
<[
ProcessState
]>>)
: states of the gateway bridges.new_bridges
(Event
<[string]
>): returns the name of the newly detected bridges.
Methods
add_all_bridges_listener(callback)
WARNING: this currently doesn't work for the secondary bridge.
Registers a callback to monitor the states of all the bridges. The callback will be registered to
an internal Event
that will behave as if the callback had been registered to all each
bridge state individually (receiving the initial state for example).
callback
:(name, state) => void
name (string)
: Process name.state
(BridgeState
): State of the bridge.
remove_all_processes_listener(callback)
Unsubscribes a callback from add_all_bridges_listener
.
Parameters
enum BridgeState
Enumeration of the available cup protocols exported by the library as BridgeState
:
Up
Down
Unknown
Gateway Aggregator ↑
A gateway aggregator represents a external MQTT broker to which the wizzilab gateways are connected through an MQTT
bridge (like the Secondary Bridge
configurable on the MQTT
web page of the gateways).
###class Gateway Aggregator ↑
Static methods
from_mqtt_client(mqtt_client, configuration)
: GatewayAggregator
mqtt_client
(mqtt.MqttClient
): MQTT client generated using the MQTT.js library.configuration
(IConfiguration
): Aggregator configuration.
connect(mqtt_conf, aggregator_configuration)
: GatewayAggregator
mqtt_configuration
(IMqttConfiguration
): Mqtt configuration to connect to the gateway.configuration
(IConfiguration
): Aggregator configuration.
Attributes
gateways
({<UID>
:NamedGateway
}): Hash map containing the aggregator's gateways. This hash map is populated upon reception of packets from the gateway.new_gateways
(Event
<[
NamedGateway
]
>): returns the name of the newly detected gateways.anomalies
(Event
<[
ErrorChain
]
>): aggregation of the anomalies caught by owned gateways + aggregator anomalies.
Methods
add_all_gateways_listener(callback)
Executes the callback once on each of the aggregator's gateways and on each gateway that might appear afterwards.
callback
:(gateway) => void
gateway
(NamedGateway
): Found gateway.
remove_all_gateways_listener(callback)
Unsubscribes a callback from add_all_gateways_listener
.
Parameters
interface IConfiguration
Description of the configuration object to pass to the gateway aggregator constructor.
gateway_configuration
: -username (string)
: MQTT username used. Necessary to respect the MQTT ACL of the gateway (else your request might end up being ignored by the gateway). -id_root (string)
: Optional. A token ID that will be inserted in all MQTT request channels ID used (to be able to track your request if you are watching them on a separate MQTT client). -no_default_random_id_root (boolean)
: Optional. Prevents the lib from auto-adding a random token in the request IDs. Beware of the request id collisions. -no_user_id_prefix (boolean)
: Optional. Prevents using theusername
in the request topic for special use cases (like emulating request from multiple users through access of the gateway via a gateway aggregator).root_topic (string)
: Topic that is considered the aggregator root. The aggregator gateway are expected to map their topics 1 level under this root topic.name_regex (string)
: Optional. Regex that the MQTT nodes must match in order to be considered gateways by the aggregator. Defaults to the UID regex.accept_root_packet (boolean)
: Optional. Stop considering as anomalies packets sent on our root topic.
class NamedGateway ↑
Class inheriting from Gateway, with the additionnal attribute name (string)
. Allows to find the
gateways in the aggregator gateways
hash map.
Common library types ↑
type HashMap<T>
↑
Represents a hash map, mapping string
s to a type T
. This is basically just a Javascript object
of which all the properties have the same type T
.
class Event ↑
Object representing an event.
WARN: The object will not check for duplicate subscriptions. Thus registering twice to the same event with the same
callback will get the callback called twice on each event. The unsub
method will also remove a single
occurence on the callback from the listening list per call.
Methods
sub(callback)
: void
Subscribes a callback to the event represented by this object.
callback
: (...args: T
) =>void
: Callback receiving the event.
unsub(callback)
: void
Removes a callback from the listener list of this event.
callback
: (...args: T
) =>void
: Callback to remove.
map<U extends unknown[]>(convert)
: Event<U>
Converts an event stream into another event stream.
convert
: (...args: T
) =>U
: Event converter.
filter(accept)
: Event
Creates a new event stream filtering some events.
accept
: (...args: T
) =>boolean
: Condition to accept events.
filter_map<U extends unknown[]>(accept)
: Event<U>
Creates an event stream filtering the original event and converting the accepted events.
convert
: (...args: T
) => [Option
][npm_monads]<U>
: Event converter.
pub(...args)
: void
WARN: You should probably never call this function unless you need to fake input for testing purpose for example. Triggers the event: sends the arguments to all the callbacks listening to this event.
...args (T)
: Event type variadic argument (depends on the event).
class State extends Event
↑
Object representing a state. It is an Event
with the last event being saved as the
current state. The only notable difference in behavior between State
and
Event
is that if sub
called after the State has been
initialized with a value, the callback passed will be called immediately once with the current state.
Also, a state, contrary to an event, will never allow more than 1 argument to be emitted (because we consider that the events associated with the state is necessarily a unique object: the next state).
value (
[Option
][npm_monads]<[...args]>)
: If no event have been received yet, this will equal [None
][npm_monads]. If an event has been received, it will contain [Some
][npm_monads](event)
, withevent
being an array containing the event variadic argument. For example, the[Gateway].version.value
will either be a [None
][npm_monads] or a [Some
][npm_monads](
AppVersion
)
.
interface IMqttConfiguration ↑
url (string)
: URL of the gateway.options (mqtt.IClientOptions)
: MQTT options of the MQTT client to connect to the gateway.subscribe_topics (Array<{path: string, qos: mqtt.QoS}>)
: Topics to register to upon connection. Example:subscribe_topics: [{"path": "/md/+/report", qos: 2}]
interface IJsonRequestOptions ↑
Mandatory:
to (number)
: Request timeout. This corresponds the maximum allowed time between 2 response packet.
Optional:
id (string)
: ID to use to send the request.callback
:(msg, meta) => void
: Callback called on each response packet.msg: any
: Received message.meta: any
: Response metadata.
<<<<<<< HEAD
class AppVersion ↑
Object representing an entity version.
id (number)
major (number)
minor (number)
patch (number)
id (string)
: ID to use to send the request.callback
:(msg, meta) => void
: Callback called on each response packet.msg: any
: Received message.meta: any
: Response metadata.
class Version ↑
Object representing an entity short version.
id (number)
major (number)
minor (number)
patch (number)
622dad3... [gateway] Add FullVersion class, and methods
class FullVersion ↑
Object representing an entity full version.
device_type (Buffer)
hardware (Buffer)
id (number)
major (number)
minor (number)
patch (number)
hash (Buffer | undefined)
maxsize (number | undefined)
Methods
constructor(id, major, minor, patch)
toString()
: string
String representation of the version (X.X.X [X]).
Static methods
from(data, offset)
: AppVersion
data (Buffer)
: Data from which to parse the version (minimum of 6 bytes).offset (number)
: Optional. Offset at which the version is in the data.
type AsyncResult<T>
↑
This type is equivalent to Promise<
[Result
][npm_monads]<T,
GatewayError.Any
>>
. It is
used in this library to return asynchronous return values of procedures that can fail.
For example, if you send an ALP READ request on a distant device with the Modem.request
method, and the gateway
couldn't reach the device, the library will receive a NO_ACK
stack status and the library will return an error of
type AlpStatus containing the property stack: {code: ALP.STACK_ERORR.NO_ACK}
.
module GatewayError ↑
Class used to represent an Error from the library. Methods of this library that can fail will return a
GatewayError.Any
which is a [discriminated union][ts_d_union] of classes inheriting from the
GatewayError.GatewayError
class.
The [discriminated union][ts_d_union] is based on the code
attribute of the errors.
class GatewayError.GatewayError
Attributes
code (GatewayError.Code)
: Error code. Each of these error codes are accessible through the enumGatewayError.Code
exposed by the library. For example, the error code of an error issued by a StreamWrite error isGatewayError.Code.StreamWrite
.
Methods
toString(verbosity): string
verbosity (number)
: (Defaults to 0)- 0 = error code + error type
- 1 = adds readable details
2 adds full error in JSON format.
Error types
StreamWrite
An error occured while trying to send an MQTT packet.
error (Error)
: Standard Javascript error issued by the MQTT.js library.
RequestIdDuplicate
The script tried to send a request with an ID that is already being used by another request currently in progress.
id (string)
: ID of the request that tried to cause an ID collision.
RequestTimeout
The request timed out.
id (string)
: ID of the request that timed out.start (Date)
: The date at which the request started.delay (number)
: The maximum delay authorized by the timeout between 2 packets.
JsonParse
The JSON request parsing of one of the responses failed.
data (Buffer)
: Data that couldn't be parsed.error (Error)
: Parser error.
GatewayJson
The "MQTT request" succeeded but the gateway responded with an error.
error
:code (number)
: Gateway error code.msg (string)
: Gateway error message.resps (object[])
: List of the responses.
ModemDriver
The "MQTT request" succeeded but the gateway modem driver responded with an error.
msg (string)
: Error message.
MissingAlpPacket
Some response packets were lost (they all have a seqnum).
missing (number[])
: List of the lost seqnums.resps (object[])
: List of the received ALP responses.
IAlpParse
The ALP parsing of one of the response packet failed.
data (Buffer)
: Data that could not be parsed.error (Error)
: Parser error.
IAlpStatus
The received response contains some bad ALP statuses. This means either some of our request actions failed, or that
we had a stack error (NO_ACK
from the device for example).
actions (Array<{id: number, code: number}>)
: Bad action statuses. Theid
corresponds to the action ID and thecode
to the STATUS code (see ALP spec).stack: {code: number}
: Optional. Error status from the gateway modem stack.resps (object[])
: List of the ALP responses.
IAlpTag
The packet has no status error but still has its TAG end of packet action containing an error flag set. This happened on older modem versions on which the stack errors were not sent to the host (and resulted in an error flag set).
resps (object[])
: List of the ALP responses.
MQTT API specification ↑
Protocol overlay ↑
The gateway currently hosts 2 types of communications entities built over the MQTT protocols:
- The notification streams: which provide a stream of data from the gateway to the clients.
- The request channels: which provides an interface that allows a client to interact with the gateway.
Notifications streams ("read only" flows)
Each notification stream can generally be described by a topic on which its data will be sent and a data format.
For example, the /md/<UID>
/report topics are notifications topics transporting raw ALP commands that corresponds
to the reports sent by other devices and received by the gateway modem <UID>
.
Requests channels
A request channel is described by 2 topics:
- the request topic: where you can write a request.
- the response topic: where you will receive a response to your request.
Unless specified otherwise, the channel topics use the following convention:
- request topic =
<channel_root_topic>/request/<user>/<request_id>
- response topic =
<channel_root_topic>/response/<user>/<request_id>
With:
channel_root_topic
: A topic representing a service, like/control
or/md/<UID>
user
: The mqtt username. This is enforced by the gateway MQTT broker ACL which will forbid you from publishing any request outside of your username sub-topics.request_id
: An ID to identify the request in case your user is sending more than one at the same time. You are to ensure the uniqueness of yourrequest_id
inside your/<user>
sub topic (which is already done by default in this library).
An extensive description of those topics is given on the Wizzilab wiki MQTT topic page.
Anomaly streams ↑
All of the classes here maintain an anomaly stream. This allows the library to track gateway abnormal states that would explain unexpected behaviors (external request id collisions, unparseable ALP payloads, etc ...).
To watch theses anomalies, these classes implement the following method:
on_anomaly(callback)
: Registers a callback to the anomaly detection stream.callback ((anomaly) => void)
anomaly
(ErrorChain
): Data describing an anomaly.
A good practice would be to register to each gateway's anomaly stream and print them in the logs.
ErrorChain ↑
Methods
toString(): string
Converts the error chain into a printable string.
Examples ↑
Gateway report watcher ↑
NodeJS ↑
const { Gateway } = require("@wizzilab/wizzigate-api");
const gateway_mqtt_conf = {
url: "mqtt://wizzigate.local:8883",
options: {
username: "user",
password: "user",
},
};
const { mqtt_client, gateway } = Gateway.connect(gateway_mqtt_conf, {
username: gateway_mqtt_conf.options.username,
});
mqtt_client.on("connect", () => {
console.log("Connected");
});
mqtt_client.on("reconnect", () => {
console.log("reconnect");
});
mqtt_client.on("close", () => {
console.log("close");
});
mqtt_client.on("offline", () => {
console.log("offline");
});
mqtt_client.on("end", () => {
console.log("end");
});
gateway.add_all_modems_listener((modem) => {
console.log("Found gateway modem: " + modem.uid);
modem.reports.add_listener((report) => {
console.log("Report:", report);
});
});
gateway.anomalies.add_listener((anomaly) => {
console.log("ANOMALY: " + anomaly);
});
Typescript ↑
import { Gateway } from "@wizzilab/wizzigate-api";
const gateway_mqtt_conf = {
url: "mqtt://wizzigate.local:8883",
options: {
username: "user",
password: "user",
},
};
const { mqtt_client, gateway } = Gateway.connect(gateway_mqtt_conf, {
username: gateway_mqtt_conf.options.username,
});
mqtt_client.on("connect", () => {
console.log("Connected");
});
mqtt_client.on("reconnect", () => {
console.log("reconnect");
});
mqtt_client.on("close", () => {
console.log("close");
});
mqtt_client.on("offline", () => {
console.log("offline");
});
mqtt_client.on("end", () => {
console.log("end");
});
gateway.add_all_modems_listener((modem) => {
console.log("Found gateway modem: " + modem.uid);
modem.reports.add_listener((report) => {
console.log("Report:", report);
});
});
gateway.anomalies.add_listener((anomaly) => {
console.log("ANOMALY: " + anomaly);
});
Aggregator report watcher ↑
NodeJS ↑
const { GatewayAggregator } = require("@wizzilab/wizzigate-api");
const aggregator_mqtt_conf = {
url: "mqtt://mybroker.com",
options: {
username: "admin",
password: "pass",
},
};
const { mqtt_client, aggregator } = GatewayAggregator.connect(
aggregator_mqtt_conf,
{
root_topic: "/gw",
gateway_configuration: {
username: aggregator_mqtt_conf.options.username,
},
},
);
mqtt_client.on("connect", () => {
console.log("Aggregator connected");
});
aggregator.add_all_gateways_listener((gateway) => {
gateway.add_all_modems_listener((modem) => {
console.log("Found gateway modem: " + modem.uid);
modem.reports.add_listener((report) => {
console.log("Report:", report);
});
});
gateway.anomalies.add_listener((anomaly) => {
console.log("ANOMALY: " + anomaly);
});
});
Typescript ↑
import { GatewayAggregator } from "@wizzilab/wizzigate-api";
const aggregator_mqtt_conf = {
url: "mqtt://mybroker.com",
options: {
username: "admin",
password: "pass",
},
};
const { mqtt_client, aggregator } = GatewayAggregator.connect(
aggregator_mqtt_conf,
{
root_topic: "/gw",
gateway_configuration: {
username: aggregator_mqtt_conf.options.username,
},
},
);
mqtt_client.on("connect", () => {
console.log("Aggregator connected");
});
aggregator.add_all_gateways_listener((gateway) => {
gateway.add_all_modems_listener((modem) => {
console.log("Found gateway modem: " + modem.uid);
modem.reports.add_listener((report) => {
console.log("Report:", report);
});
});
gateway.anomalies.add_listener((anomaly) => {
console.log("ANOMALY: " + anomaly);
});
});
Distant device version reader ↑
NodeJS ↑
const { Modem, Gateway, AppVersion } = require("@wizzilab/wizzigate-api");
const { Err, Ok } = require("@usefultools/monads");
const AT = require("alp-tools");
const REQUEST_TIMEOUT = 10 * 1000;
const FID = {
UID: 0,
VERSION: {
MODEM: 2,
HOST: 65,
},
};
const VERSION_OFFSET = 12;
const gateway_mqtt_conf = {
url: "mqtt://wizzigate.local:8883",
options: {
username: "user",
password: "user",
},
};
const alp_builder = new AT.PacketBuilder();
// Setting default ALP commands parameters (make the packet building less painful)
alp_builder.set_default_params({
tag: { eop: true, tag_id: 0 },
read_file: { resp: true, group: false, file_offset: 0 },
write_file: { resp: true, group: false, file_offset: 0 },
fwd: {
resp: true,
group: false,
itf_id: 0xd7,
stop_on_err: false,
record: false,
retry_mode: 0,
resp_mode: 1,
dorm_to: 0,
te: 0,
addr: {
addr_type: 2,
nls_method: 6,
estimated_reached: 4,
},
},
});
const { mqtt_client, gateway } = Gateway.connect(gateway_mqtt_conf, {
username: gateway_mqtt_conf.options.username,
});
mqtt_client.on("connect", () => {
console.log("Connected");
});
async function read_version(modem, target) {
// Build ALP command
const distant_read_command = alp_builder
.tag()
.fwd({
addr: {
id: target,
access_class: 0x01,
},
})
.read_file({
file_id: FID.VERSION.MODEM,
file_offset: VERSION_OFFSET,
length: AppVersion.size(),
})
.read_file({
file_id: FID.VERSION.HOST,
file_offset: VERSION_OFFSET,
length: AppVersion.size(),
})
.buffer();
// Send the request
const result = await modem.request(distant_read_command, {
to: REQUEST_TIMEOUT,
});
// Process the result
if (result.is_ok()) {
const responses = result.unwrap();
// Responses packet description ---------------------------------------------
// ITF_FULL status (payload successfully sent to the DASH7 stack of the modem)
// const itf_full_status_packet = responses[0];
// Response packet. This is the informations we want.
const response_packet = responses[1];
// End of transaction packet (TAG_RSP + STATUS)
// const end_packet = responses[2];
// Response packet actions descriptions -------------------------------------
// Tag to match the response to our request
// const tag_rsp = response_packet[0];
// Interface status (address of the source, LB, rx_lev, etc...)
// const istatus = response_packet[1];
// Reads we requested
const read_1 = response_packet[2];
const read_2 = response_packet[3];
// Parse the response
return Ok({
modem: AppVersion.from(read_1.data),
host: AppVersion.from(read_2.data),
});
} else {
return Err(result.err());
}
}
let emitting_modem_uid;
// Catch the user's inputs
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.on("line", async (input) => {
// Check that the UID has a good format
const filtered_input = input.match(/^[0-9A-Fa-f]{16}/);
if (!filtered_input) {
console.log(
"Bad uid. The uid should consist of 16 hexadecimal digits.",
);
return;
}
// Check that we already have discovered a gateway modem to send the ALP requests
if (!emitting_modem_uid) {
return;
}
// Parse UID
const uid = Buffer.from(filtered_input[0].slice(0, 16), "hex");
// Try to read the versions of the device
console.log("Reading versions of " + uid.toString("hex").toUpperCase());
let result;
try {
result = await read_version(gateway.modems[emitting_modem_uid], uid);
// Handle promise exceptions
} catch (e) {
console.log(e.stack);
return process.exit(-1);
}
// On success, print the versions
if (result.is_ok()) {
const versions = result.unwrap();
console.log("Modem: " + versions.modem.toString());
console.log("Host : " + versions.host.toString());
// On failure, print the error
} else {
console.log(
"Could not read the versions: " + result.unwrap_err().toString(),
);
}
return;
});
const known_devices = {};
gateway.add_all_modems_listener((modem) => {
console.log("Found gateway modem: " + modem.uid);
// Save the modem as the emitting modem
if (!emitting_modem_uid) {
console.log(
"You can now read versions of distant devices by entering their UIDs in the console.",
);
}
emitting_modem_uid = modem.uid;
// Prevent the gateway modem to be declared as discovered by itself (non pertinent)
known_devices[modem.uid] = true;
// Print the discovery of devices sending reports to this gateway
modem.reports.add_listener((report) => {
const first_packet = report[0];
// Only process report starting with an ISTATUS
if (
first_packet.opcode !== AT.SPEC.ACTIONS.STATUS.opcode &&
first_packet.opcode.ext !== 1
) {
return;
}
// Save the device and print it if it is not known
const uid = first_packet.addr.id.toString("hex").toUpperCase();
if (!known_devices[uid]) {
known_devices[uid] = true;
console.log(
"Modem " +
modem.uid +
": Discovered device " +
uid +
" LB " +
report[0].lb,
);
}
});
});
gateway.anomalies.add_listener((anomaly) => {
console.log("ANOMALY: " + anomaly);
});
Typescript ↑
import { Modem, Gateway, AppVersion } from "@wizzilab/wizzigate-api";
import { Err, Ok } from "@usefultools/monads";
// tslint:disable:no-var-requires
const AT = require("alp-tools");
// tslint:enable:no-var-requires
const REQUEST_TIMEOUT = 10 * 1000;
const FID = {
UID: 0,
VERSION: {
MODEM: 2,
HOST: 65,
},
};
const VERSION_OFFSET = 12;
const gateway_mqtt_conf = {
url: "mqtt://wizzigate.local:8883",
options: {
username: "user",
password: "user",
},
};
const alp_builder = new AT.PacketBuilder();
// Setting default ALP commands parameters (make the packet building less painful)
alp_builder.set_default_params({
tag: { eop: true, tag_id: 0 },
read_file: { resp: true, group: false, file_offset: 0 },
write_file: { resp: true, group: false, file_offset: 0 },
fwd: {
resp: true,
group: false,
itf_id: 0xd7,
stop_on_err: false,
record: false,
retry_mode: 0,
resp_mode: 1,
dorm_to: 0,
te: 0,
addr: {
addr_type: 2,
nls_method: 6,
estimated_reached: 4,
},
},
});
const { mqtt_client, gateway } = Gateway.connect(gateway_mqtt_conf, {
username: gateway_mqtt_conf.options.username,
});
mqtt_client.on("connect", () => {
console.log("Connected");
});
async function read_version(modem: Modem, target: Buffer) {
// Build ALP command
const distant_read_command = alp_builder
.tag()
.fwd({
addr: {
id: target,
access_class: 0x01,
},
})
.read_file({
file_id: FID.VERSION.MODEM,
file_offset: VERSION_OFFSET,
length: AppVersion.size(),
})
.read_file({
file_id: FID.VERSION.HOST,
file_offset: VERSION_OFFSET,
length: AppVersion.size(),
})
.buffer();
// Send the request
const result = await modem.request(distant_read_command, {
to: REQUEST_TIMEOUT,
});
// Process the result
if (result.is_ok()) {
const responses = result.unwrap();
// Responses packet description ---------------------------------------------
// ITF_FULL status (payload successfully sent to the DASH7 stack of the modem)
// const itf_full_status_packet = responses[0];
// Response packet. This is the informations we want.
const response_packet = responses[1];
// End of transaction packet (TAG_RSP + STATUS)
// const end_packet = responses[2];
// Response packet actions descriptions -------------------------------------
// Tag to match the response to our request
// const tag_rsp = response_packet[0];
// Interface status (address of the source, LB, rx_lev, etc...)
// const istatus = response_packet[1];
// Reads we requested
const read_1 = response_packet[2];
const read_2 = response_packet[3];
// Parse the response
return Ok({
modem: AppVersion.from(read_1.data),
host: AppVersion.from(read_2.data),
});
} else {
return Err(result.err());
}
}
let emitting_modem_uid: string | undefined;
// Catch the user's inputs
import * as readline from "readline";
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.on("line", async (input: string) => {
// Check that the UID has a good format
const filtered_input = input.match(/^[0-9A-Fa-f]{16}/);
if (!filtered_input) {
console.log(
"Bad uid. The uid should consist of 16 hexadecimal digits.",
);
return;
}
// Check that we already have discovered a gateway modem to send the ALP requests
if (!emitting_modem_uid) {
return;
}
// Parse UID
const uid = Buffer.from(filtered_input[0].slice(0, 16), "hex");
// Try to read the versions of the device
console.log("Reading versions of " + uid.toString("hex").toUpperCase());
let result;
try {
result = await read_version(gateway.modems[emitting_modem_uid], uid);
// Handle promise exceptions
} catch (e) {
console.log(e.stack);
return process.exit(-1);
}
// On success, print the versions
if (result.is_ok()) {
const versions = result.unwrap();
console.log("Modem: " + versions.modem.toString());
console.log("Host : " + versions.host.toString());
// On failure, print the error
} else {
console.log(
"Could not read the versions: " + result.unwrap_err().toString(),
);
}
return;
});
const known_devices: { [name: string]: boolean } = {};
gateway.add_all_modems_listener((modem) => {
console.log("Found gateway modem: " + modem.uid);
// Save the modem as the emitting modem
if (!emitting_modem_uid) {
console.log(
"You can now read versions of distant devices by entering their UIDs in the console.",
);
}
emitting_modem_uid = modem.uid;
// Prevent the gateway modem to be declared as discovered by itself (non pertinent)
known_devices[modem.uid] = true;
// Print the discovery of devices sending reports to this gateway
modem.reports.add_listener((report) => {
const first_packet = report[0];
// Only process report starting with an ISTATUS
if (
first_packet.opcode !== AT.SPEC.ACTIONS.STATUS.opcode &&
first_packet.opcode.ext !== 1
) {
return;
}
// Save the device and print it if it is not known
const uid = first_packet.addr.id.toString("hex").toUpperCase();
if (!known_devices[uid]) {
known_devices[uid] = true;
console.log(
"Modem " +
modem.uid +
": Discovered device " +
uid +
" LB " +
report[0].lb,
);
}
});
});
gateway.anomalies.add_listener((anomaly) => {
console.log("ANOMALY: " + anomaly);
});
Changelog ↑
v2.1 ↑
- Add Gateway.from_mqtt_client static method
v2 ↑
- Use @usefultools/monads instead of deprecated @threestup/monads package. This is a breaking
change as the
Result
type API has changed: - The old.ok()
is equivalent to the new.unwrap()
- The old.err()
is equivalent to the new.unwrap_err()
- Refactor the event system. This is a breaking change as this removes all the previous hook methods. - The gateway modules can now have
Event
and/orState
attributes. Those attributes are the new hooks. - The entities aggregating multiple other entities (the GatewayAggregator aggregating gateways, the Gateway aggregating modems, etc ...) still have a convenient method to register a callback on all of the sub entities, but it has been renamedadd_all_*_listener
and now has a dual method allowing unregistering the callback. - Renaming: - The old Version object is now called AppVersion. The new Version object does not include the id field (which might not always be relevant or present).
[wiki_cup]: http://wizzilab.com/wiki/#!solutions/mqtt_topics.md#III)Code_Upgrade_Topics(CUP) [ts_d_union]: https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html [npm_monads]: https://www.npmjs.com/package/@usefultools/monads [npm_alp_lib]: https://www.npmjs.com/package/alp-lib