Skip to content
Matt Magoffin edited this page Sep 17, 2024 · 12 revisions

SolarFlux is a real-time publish/subscribe system based on the MQTT protocol. SolarNodes can publish the data they collect to SolarFlux and then applications can subscribe to receive that data from SolarFlux.

You can try out the SolarFlux Demo app to see the datum being captured by any of your nodes.

Publishing: SolarNode datum

SolarNode devices can post data to SolarFlux via the SolarFlux Upload Service plugin. Nodes use their ID as the MQTT client ID and also provide their client certificate on mqtts connections.

Messages can be published with the MQTT retained flag set, which means the most recently published datum is saved by SolarFlux. When an application subscribes to a topic it will immediately receive any retained message for that topic. In this way, SolarFlux can provide a "most recent" snapshot of all datum across all nodes and sources.

Datum topic structure

The SolarFlux Upload Service plugin posts every datum captured by other plugins up to SolarFlux, using MQTT topics named with the node ID and datum source ID, according to this pattern:

node/N/datum/A/S

In this pattern N is a node ID , A is an aggregate key, and S is a source ID. SolarNode datum are always published with a 0 aggregate key which represents the raw (or no) aggregation level. Note that any leading / in a source ID is stripped from the topic name.

Example topics look like:

node/1/datum/0/Meter
node/2/datum/0/Building1/Room1/Light1
node/2/datum/0/Building1/Room1/Light2

Datum message format

The datum messages published by the SolarFlux Upload Service plugin are CBOR encoded indefinite-length maps. These are easily converted to JSON objects. The map keys are the datum property names. All datum have the following properties at a minimum:

Property Type Description
sourceId String The SolarNetwork source ID.
created Number The datum date as milliseconds since the epoch, typically the time it was captured at.

You might also see the following properties:

Property Type Description
_DatumType String The SolarNode internal name of the primary datum type.
_DatumTypes Array<String> An array of SolarNode internal names of all datum types the datum implements.

Here's an example datum message, expressed as JSON:

{
  "_DatumType": "net.solarnetwork.node.domain.ACEnergyDatum",
  "_DatumTypes": [
    "net.solarnetwork.node.domain.ACEnergyDatum",
    "net.solarnetwork.node.domain.EnergyDatum",
    "net.solarnetwork.node.domain.Datum",
    "net.solarnetwork.node.domain.GeneralDatum"
  ],
  "apparentPower": 2797,
  "created": 1545167905344,
  "current": 11.800409317016602,
  "phase": "PhaseB",
  "phaseVoltage": 409.89337158203125,
  "powerFactor": 1.2999000549316406,
  "reactivePower": -1996,
  "realPower": 1958,
  "sourceId": "Ph2",
  "voltage": 236.9553680419922,
  "watts": 1958
}

Publishing: SolarNet aggregate datum

SolarNetwork automatically publishes aggregate datum to SolarFlux, regardless if SolarNodes are publishing raw datum to SolarFlux. SolarNetwork continuously computes and updates the aggregate data and publishes those updates to SolarFlux.

Messages are published with the MQTT retained flag set, which means the most recently published datum is saved by SolarFlux. When an application subscribes to a topic it will immediately receive any retained message for that topic. In this way, SolarFlux will provide a "most recent" snapshot of all aggregate datum across all nodes and sources.

The MQTT topics used follow the same pattern as outlined previously:

node/N/datum/A/S

In this pattern N is a node ID , A is an aggregate key, and S is a source ID. Note that any leading / in a source ID is stripped from the topic name. The aggregate key values supported by SolarFlux are:

Aggregate Topic Key Description
Hour h The current hour.
Day d The current day, in the time zone of the node the topic is for.
Month M The current month, in the time zone of the node the topic is for.

Example topics look like:

node/1/datum/h/Meter
node/2/datum/d/Building1/Room1/Light1
node/2/datum/M/Building1/Room1/Light2

The messages are CBOR encoded objects in the same form as outlined for the raw datum messages previously. They might contain additional aggregate properties derived from the raw data, as calculated by SolarNetwork.

All aggregate datum have the following properties at a minimum:

Property Type Description
nodeId Number The node ID.
sourceId String The datum source ID.
created Number The aggregate datum date as milliseconds since the epoch.
localDate String The aggregate datum date as yyyy-MM-dd format in the time zone of the node.
localTime String The aggregate datum time as HH:mm format in the time zone of the node.

Here's an example aggregate datum message, expressed as JSON:

{
  "created": 1573023600000,
  "nodeId": 108,
  "sourceId": "DB",
  "localDate": "2019-11-06",
  "localTime": "20:00",
  "watts": 2097.409,
  "watts_max": 3776,
  "watts_min": 1118,
  "wattHours": 2005.855,
  "wattHoursReverse": 0
}

Publishing: User Events

SolarNetwork-generated user events are published to SolarFlux, and can be subscribed to with a User security token. The MQTT topics used follow this pattern:

user/U/event/T

In this pattern U is a user ID and T is an event tag. There can be any number of tags with an event, so multiple /T sub-topic levels can be included.

For example, the MQTT topic would be user/123/event/ocpp/charger/forbidden for the following user event:

{
  "created" : 1716593105081,
  "data" : {
    "cp" : "chgr-001",
    "error" : "HTTP Authorization header not provided (no credentials provided)."
  },
  "eventId" : "018facec-18b9-7afc-b722-d2f1bd371d3c",
  "tags" : [ "ocpp", "charger", "forbidden" ],
  "userId" : 123
}

Subscribing

Applications wishing to subscribe to SolarFlux data can do so by using MQTT, directly or via a WebSocket connection.

Subscribing: MQTT

For applications that can access MQTT directly, the connection details for the default SolarFlux service are as follows:

Property Value Description
URL mqtts://fluxion.solarnetwork.net:8885 The MQTT URL to connect to. This uses TLS encryption.
Client ID token + This must start with the same token value used for the Username property, followed by anything. The maximum length is 40 characters.
Username token A SolarNetwork security token.
Password secret or signature A SolarNetwork security token or a SolarNetwork V2 signature derived from the token and its associated secret, extended with a Date=D attribute.

Example subscribe script

Here's an example shell script that uses mosquitto_sub to subscribe to SolarFlux, xxd to decode the payload from hex, and cbor2json.rb to decode the CBOR into JSON, sort of like tail -f for SolarFlux:

#!/bin/sh

mosquitto_sub -V mqttv31 --cafile /usr/local/etc/openssl/cert.pem \
	-h fluxion.solarnetwork.net -p 8885 -q 0 \
	-i '<<security token>>_<<random suffix>>' \
	-u '<<security token>>' \
	-P '<<token secret>>' \
	-F '%t %x' \
	-t 'node/+/datum/0/#' | (
	while read -r LINE; do
		echo "$(date)" $(echo "$LINE" |cut -d' ' -f1) $(echo "$LINE" |cut -d' ' -f2 |xxd -r -p |cbor2json.rb)
  	done;
)

Replace <<security token>> and <<random suffix>> and <<token secret>> with valid credentials. Example output looks like:

Sat 14 Dec 2019 16:54:16 NZDT user/1/node/2/datum/0/Main { "sourceId": "Main", "phase": "Total",
  "reactivePower": -289, "created": 1576295598681, "apparentPower": 590,
  "frequency": 49.967018127441406, "voltage": 238.205810546875, "current": 2.476088285446167,
  "effectivePowerFactor": -0.8716593384742737, "watts": 515, "realPower": 515,
  "wattHours": 34688636, "wattHoursReverse": 0, "powerFactor": 1.1283409595489502 }

Subscribing: MQTT over WebSocket

For applications such as web apps that cannot access MQTT directly, MQTT via a WebSocket connection can be used. The connection details for the default SolarFlux service are:

Property Value Description
URL wss://flux.solarnetwork.net/mqtt The WebSocket URL to connect to. This uses TLS encryption.
Client ID token + This must start with the same token value used for the Username property, followed by anything. The maximum length is 40 characters.
Username token A SolarNetwork security token.
Password secret or signature A SolarNetwork security token or a SolarNetwork V2 signature derived from the token and its associated secret, extended with a Date=D attribute.

Client ID considerations

Some consideration must be given to the Client ID used when connecting to SolarFlux, as they must be unique across all connections to SolarFlux. If you connect to SolarFlux with the same Client ID as one that is already used by another connection, the previous connection will be dropped. Often a randomized Client ID value is appropriate.

The SolarFlux Demo app appends a random UUID (to the required token prefix) for each connection, which is a pretty safe way to ensure no two clients use the same Client ID value for an application where the number of connecting clients is not limited.

If you are developing an application that maintains a stable connection to SolarFlux, then it can be safe to append a simple static value that uniquely identifies the application to the required token prefix.

Password signature values

Using token signature passwords can be advantageous for applications that aren't allowed access to a token secret directly. In this situation, some external mechanism must be provided that can give the application either a token signing key for the application to generate the signature with, or the actual signature value. The password signature is a SolarNetwork V2 signature, extended with a Date=D attribute. The request data used to sign the token request must be the following:

Property Value Description
Verb GET The canonical request HTTP verb.
Path /solarflux/auth The canonical request URI.
Host data.solarnetwork.net The signed Host header value.
Date The signed X-SN-Date header value. The date must also be provided in the final signature as a Date=D attribute. See below for more details.

Thus the canonical headers are Host and X-SN-Date and these must both be included as signed headers in the signature.

The final signature value must include an additional Date=D attribute, where D is a Unix epoch value, i.e. the number of seconds since 1 Jan, 1970. Note this is not specified in milliseconds, as is the default in programming languages like Java and JavaScript.

An example MQTT password thus looks like this:

SNWS2 Credential=a09sjds09wu9wjsd9uy2,SignedHeaders=host;x-sn-date,Signature=4d56e33d69300c163f414ee688e9771eabce45d5a425e480a8cc605e97263919,Date=1545069502

The signature can be shortened to include just the Signature and Date attributes, if desired. Thus the following MQTT password is equivalent to the previous example:

Signature=4d56e33d69300c163f414ee688e9771eabce45d5a425e480a8cc605e97263919,Date=1545069502

Also note the the order of the attributes is not significant. Thus the following MQTT password is equivalent to the previous example:

Date=1545069502,Signature=4d56e33d69300c163f414ee688e9771eabce45d5a425e480a8cc605e97263919
Clone this wiki locally