Skip to content

Instantly share code, notes, and snippets.

@hyun007
Last active May 21, 2024 03:30
Show Gist options
  • Save hyun007/c689fbed10424b558f140c54851659e3 to your computer and use it in GitHub Desktop.
Save hyun007/c689fbed10424b558f140c54851659e3 to your computer and use it in GitHub Desktop.
span.io api documentation

Span.io API Documentation

The Span API does not at this time require any authentication. This will probably change in the future.

Description: Gets panel summary, firmware version, door state, serial number, network status Request: GET /api/v1/status Response:

{
  "software": {
    "firmwareVersion": "spanos2/r202216/04",
    "updateStatus": "idle",
    "env": "prod"
  },
  "system": {
    "manufacturer": "Span",
    "serial": "nt-2139-c1ac4",
    "model": "00200",
    "doorState": "OPEN",
    "uptime": 1475028
  },
  "network": {
    "eth0Link": false,
    "wlanLink": true,
    "wwanLink": false
  }
}

Description: Gets panel state, grid state, power draw for whole panel, and for each breaker Request: GET /api/v1/panel Response:

{
  "mainRelayState": "CLOSED",
  "instantGridPowerW": 8361.962890625,
  "feedthroughPowerW": -82.79021826386452,
  "gridSampleStartMs": 836674,
  "gridSampleEndMs": 836686,
  "dsmGridState": "DSM_GRID_UP",
  "dsmState": "DSM_ON_GRID",
  "currentRunConfig": "PANEL_ON_GRID",
  "branches": [
    {
      "id": 1,
      "relayState": "CLOSED",
      "instantPowerW": -3.015791177749634,
      "importedActiveEnergyWh": 1.4696391820907593,
      "exportedActiveEnergyWh": 12594.255859375
    },
    {
      "id": 2,
      "relayState": "CLOSED",
      "instantPowerW": -8.53760051727295,
      "importedActiveEnergyWh": 343.6731262207031,
      "exportedActiveEnergyWh": 19945.1328125
    },
    {
      "id": 3,
      "relayState": "CLOSED",
      "instantPowerW": -4.699003219604492,
      "importedActiveEnergyWh": 27.433591842651367,
      "exportedActiveEnergyWh": 6132.89697265625
    },
	...
  ]
}

Description: Get information on individual breakers, their positions, names, state, priority Request: GET /api/v1/circuits Response:

{
  "spaces": {
    "xxxxxxxxxxxxxxx": {
      "id": "xxxxxxxxxxxxxxx",
      "name": "Garage 220",
      "relayState": "CLOSED",
      "instantPowerW": -3625.2879638671875,
      "instantPowerUpdateTimeS": 1656531017,
      "importEnergyAccumWh": 4146.5565185546875,
      "exportEnergyAccumWh": 313066.625,
      "energyAccumUpdateTimeS": 1656530716,
      "tabs": [
        12,
        14
      ],
      "priority": "NOT_ESSENTIAL",
      "is_user_controllable": true,
      "is_sheddable": false,
      "is_never_backup": false
    },
    "xxxxxxxxxxxxxxx": {
      "id": "xxxxxxxxxxxxxxx",
      "name": "A/C condeser",
      "relayState": "CLOSED",
      "instantPowerW": -2428.6781005859375,
      "instantPowerUpdateTimeS": 1656531017,
      "importEnergyAccumWh": 11638.456787109375,
      "exportEnergyAccumWh": 752839.09375,
      "energyAccumUpdateTimeS": 1656530716,
      "tabs": [
        16,
        18
      ],
      "priority": "MUST_HAVE",
      "is_user_controllable": true,
      "is_sheddable": false,
      "is_never_backup": false
    },
    "xxxxxxxxxxxxxxx": {
      "id": "xxxxxxxxxxxxxxx",
      "name": "Tesla charger",
      "relayState": "CLOSED",
      "instantPowerW": -503.9554138183594,
      "instantPowerUpdateTimeS": 1656531017,
      "importEnergyAccumWh": 30.012138724327087,
      "exportEnergyAccumWh": 218781.4375,
      "energyAccumUpdateTimeS": 1656530716,
      "tabs": [
        26,
        28
      ],
      "priority": "NICE_TO_HAVE",
      "is_user_controllable": true,
      "is_sheddable": false,
      "is_never_backup": false
    },
    ...
  }
}

Description: Change breaker state, turn on/off a breaker Valid Values: OPEN, CLOSED Request: POST /api/v1/circuits/xxxxxxxxxxxxxxx

{
	"relay_state_in": {
		"relayState":"OPEN"
	}
}

Response:

{
	"id":"xxxxxxxxxxxxxxx",
	"name":"Garage outlets*",
	"relayState":"OPEN",
	"instantPowerW":0.0,
	"instantPowerUpdateTimeS":1656538555,
	"importEnergyAccumWh":722.7332153320312,
	"exportEnergyAccumWh":973.8363037109375,
	"energyAccumUpdateTimeS":1656538493,
	"tabs":[6],
	"priority":"NOT_ESSENTIAL",
	"is_user_controllable":true,
	"is_sheddable":false,
	"is_never_backup":false
}

Description: Change breaker priority Valid Values: MUST_HAVE, NICE_TO_HAVE, NOT_ESSENTIAL Request: POST /api/v1/circuits/xxxxxxxxxxxxxxx

{
	"priority_in": {
		"priority":"NICE_TO_HAVE"
	}
}

Response:

{
	"id":"xxxxxxxxxxxxxxx",
	"name":"Garage outlets*",
	"relayState":"OPEN",
	"instantPowerW":0.0,
	"instantPowerUpdateTimeS":1656539097,
	"importEnergyAccumWh":722.7332153320312,
	"exportEnergyAccumWh":973.8363037109375,
	"energyAccumUpdateTimeS":1656538493,
	"tabs":[6],
	"priority":"NICE_TO_HAVE",
	"is_user_controllable":true,
	"is_sheddable":false,
	"is_never_backup":false
}
@smatsmats
Copy link

smatsmats commented Oct 29, 2022

hey look what i found ...

(this is all unverified, so far some of it works for me, some does not)


Register New Client

POST   /api/v1/auth/clients


Register New Client POST

/api/v1/auth/register
This API enables a new client to be registered with the SPAN Panel, including information such as name and description. In return the SPAN Panel returns an access token which is to be included with all other API requests. Below is an example of the command using the initial access token and where name = SPAN_API_User_2, and description = SPAN_API_User_2 description.
$ curl -X 'POST'
'http://192.168.40.30/api/v1/auth/register'
-H 'accept: application/json'
-H 'Content-Type: application/json'
-d '{
"name": "SPAN_API_User_2",
"description": "SPAN_API_User_2 description",
}'

The response is a 200 on success, or a 422 Validation Error.

The annotated 200 response schema is as follows where:
name – user defined name for the client to be added
description – user defined description of the client to be added

{
  "name": "string",
  "description": "string"
}

Example: annotated 422 response schema

{
  "detail": [
    {
      "loc": [
        "string"
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}

Example of a 200 (success) response:

{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdHJpbmciLCJpYXQiOjE2NjI2MTEyODV9.tekwsKeaT9GW7bRrpOcyVxWRHn21qQOX897F4Zp-du0",
  "tokenType": "bearer"
}

Take note of the accessToken string as this will be required for all other API interactions.


View All Clients

GET   /api/v1/auth/clients

This API returns the list of all authenticated clients registered with the SPAN Panel, including information such as name, description, and access control group. Below is an example of the command using the initial accessToken.
$ curl -X 'GET'
'http://192.168.40.30/api/v1/auth/clients/'
-H 'accept: application/json'
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJTaG9vcCIsImlhdCI6MTY2MjU5NDAxNn0.WH2_9gfz7YSsnRymPERWmfn_ic5x4BLvKfkO2ByzMxk'

The response is a 200 on success.

The annotated 200 response schema is as follows, where:
additionalProp# - returns the user defined name of the client added
description – returns a user defined short description for the client added
issued_at – returns the epoch date and time the client was added as an authorized client. This may be converted into a human readable date and time using an epoch converter.
allowed_endpoint_groups – returns the permission settings for each endpoint function; Delete, Get, Post, and Push.

{
  "clients": {
    "additionalProp1": {
      "description": "string",
      "issued_at": 0,
      "allowed_endpoint_groups": {
        "delete": [
          "string"
        ],
        "get": [
          "string"
        ],
        "post": [
          "string"
        ],
        "push": [
          "string"
        ]
      }
    }
  }
}

An example 200 response is shown below, listing one client “SPAN_API_User_1” has been authorized for “SPAN API User 1” at issuance date of 1662561023 or Sept 7, 2022 at 2:30P GMT, and contains the “homeowner” permission settings for each API function.

{
  "clients": {
    "SPAN_API_User_1": {
      "description": "SPAN API User1",
      "issued_at": 1662561023,
      "allowed_endpoint_groups": {
        "delete": [
          "homeowner"
        ],
        "get": [
          "homeonwer"
        ],
        "post": [
          "homeowner"
        ],
        "push": [
          "homeowner"
        ]
      }
    }
  }
}


View {name} Client

GET   /api/v1/auth/clients/{name}

This API returns the status of the specified client registered with the SPAN Panel, including information such as description and authentication date. Below is an example of the command using the initial accessToken.

$ curl -X 'GET'
'http://192.168.40.30/api/v1/auth/clients/SPAN_API_User_1'
-H 'accept: application/json'
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJTaG9vcCIsImlhdCI6MTY2MjU5NDAxNn0.WH2_9gfz7YSsnRymPERWmfn_ic5x4BLvKfkO2ByzMxk'

The response is a 200 on success and 422 on error.

The annotated 200 response schema is as follows, where:
additionalProp# - returns the user defined name of the client added
description – returns a user defined short description for the client added
issued_at – returns the epoch date and time the client was added as an authorized client. This may be converted into a human readable date and time using an epoch converter.
allowed_endpoint_groups – returns the access control settings for API request types made by the client.

{
  "description": "string",
  "issued_at": 0,
  "allowed_endpoint_groups": {
    "delete": [
      "string"
    ],
    "get": [
      "string"
    ],
    "post": [
      "string"
    ],
    "push": [
      "string"
    ]
  }
}

The annotated 422 response schema is as follows:

{
  "detail": [
    {
      "loc": [
        "string"
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}
}

An example 200 response is shown below, listing one client “SPAN_API_User_1” has been authorized for “SPAN API User1” at issuance date of 1662561023 or Sept 7, 2022 at 2:30P GMT, and contains the “homeowner” permission settings for each API function.

{
  "clients": {
    "SPAN_API_User_1": {
      "description": "SPAN_Product_Management",
      "issued_at": 1662561023,
      "allowed_endpoint_groups": {
        "delete": [
          "homeowner"
        ],
        "get": [
          "homeonwer"
        ],
        "post": [
          "homeowner"
        ],
        "push": [
          "homeowner"
        ]
      }
    }
  }
}


Delete {name} Client

DELETE   /api/v1/auth/clients/{name}

This API enables a specified client, short description and associated accessToken to be removed from the registry of the SPAN Panel. Below is an example of the command using the initial accessToken.

$ curl -X 'DELETE'
'http://192.168.40.30/api/v1/auth/clients/SPAN_API_User_1'
-H 'accept: application/json'
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJTaG9vcCIsImlhdCI6MTY2MjU5NDAxNn0.WH2_9gfz7YSsnRymPERWmfn_ic5x4BLvKfkO2ByzMxk'

The response is a 200 on success and a 422 on Error.

The annotated 200 response schema is as follows, where:
additionalProp# - returns the user defined name of the client added
description – returns a user defined short description for the client added
issued_at – returns the epoch date and time the client was added as an authorized client. This may be converted into a human readable date and time using an epoch converter.
allowed_endpoint_groups – returns the access control settings for API request types made by the client.

{
  "description": "string",
  "issued_at": 0,
  "allowed_endpoint_groups": {
    "delete": [
      "string"
    ],
    "get": [
      "string"
    ],
    "post": [
      "string"
    ],
    "push": [
      "string"
    ]
  }
}

The annotated 422 response schema is as follows:

{
  "detail": [
    {
      "loc": [
        "string"
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}

An example 200 response is shown below, listing one client “SPAN_API_User_1” has been authorized for “SPAN API User1” at issuance date of 1662561023 or Sept 7, 2022 at 2:30P GMT, and contains the “homeowner” permission settings for each API function.

{
  "clients": {
    "SPAN_API_User_1": {
      "description": "SPAN API User1",
      "issued_at": 1662561023,
      "allowed_endpoint_groups": {
        "delete": [
          "homeowner"
        ],
        "get": [
          "homeonwer"
        ],
        "post": [
          "homeowner"
        ],
        "push": [
          "homeowner"
        ]
      }
    }
  }
}

@nmcbride
Copy link

nmcbride commented Dec 8, 2022

Thanks guys this set me down the right path for what I needed.

@smatsmats

Looks like for the POST to clients they are not allowing POST anymore.

@nmcbride
Copy link

nmcbride commented Dec 8, 2022

@smatsmats

Oh I see.
The POST to /api/v1/auth/clients has been changed to a POST to /api/v1/auth/clients/register.

GET /api/v1/auth/clients/CLIENT doesn't seem to be valid for me.

@smatsmats
Copy link

@nmcbride
Thanks, I think you mean POST to /api/v1/auth/register

I updated the above documentation.

I agree that GET /api/v1/auth/clients/CLIENT returns a 500 when the client exists. That's something Span needs to fix.

fwiw I'm on 'firmwareVersion': 'spanos2/r202240/06.1',

@j4ys0n
Copy link

j4ys0n commented Jun 27, 2023

anyone find endpoints with info about solar? i've got inverters hooked up to my panel but that specific breaker is omitted from the circuits endpoint.

@smatsmats
Copy link

Agree it's an issue. The circuit is omitted from /circuits but some info is included in /panel. I shove instantPowerW into a database and look at it over time.

{ 'exportedActiveEnergyWh': 164.51783752441406,
'id': 32,
'importedActiveEnergyWh': 146344.125,
'instantPowerW': 2463.74658203125,
'relayState': 'CLOSED'}

@prs999
Copy link

prs999 commented Nov 24, 2023

Hi! I am using firmwareVersion=spanos2/r202342/04. I am unable register a user using the api:

curl -X "POST" "http://$tgt/api/v1/auth/register" -H "accept: application/json" -H "Content-Type: application/json" -H "Password: xxxxxxxx"
-d '{"name": "SPAN_API_User_2", "description": "SPAN_API_User_2 description" }'

I added the -H "Password: xxxxxxxx" without success or difference from the SPAN API.

The return response I am getting with/without the password is:
{"detail":"Must provide one-time-password, dashboard password or door-bypass"}

Are you able to use the latest firmwareVersion?

V/R,
Paul

@smatsmats
Copy link

I think you still need to do the 3 (I think) button pushes to do the registration calls.

I'm probably still using a token snagged from a web browser login.

@prs999
Copy link

prs999 commented Dec 1, 2023

Could you please provide more details --- 3 button pushes?!? What are you referring to? How did you login from the browser?

@patniemeyer
Copy link

patniemeyer commented Dec 17, 2023

Could you please provide more details --- 3 button pushes?!? What are you referring to? How did you login from the browser?

Find the local IP address using a network scanner or perhaps the DHCP table on your router. The web UI login page will appear on that IP. It prompts for a password or instructs that you can push the door button (the switch at the top that closes when you close the door) three times to open up access for a set time. You can then log into the web UI without the password. At that point you can see the calls that it is making by e.g. using your web browsers developer tools... Look at the request header of one of the /circuits requests and just copy the bearer token from it. Paste it in replacing the ones in the examples above, e.g.

curl -X GET "http://[MY IP]/api/v1/circuits" -H "Authorization: bearer [MY TOKEN]“ 

@patniemeyer
Copy link

For fun I have made a little CLI tool that shows circuits data in a sorted table, kind of like the "top" command. python script. Thanks to @hyun007 for his work on the API.

@BrianLawes
Copy link

BrianLawes commented Jan 5, 2024

Thanks hyun007 and patneimeyer. This script and all the info is great.

I modified patneimeyer's script to call /api/vi/panel, save it to a json file, and get the below info as part of the content. Any idea what the relayState means? All of mine are showing CLOSED for all 32 id fields.

..
"branches": [
{
"id": 1,
"relayState": "CLOSED",
"instantPowerW": 0.9801977276802063,
"importedActiveEnergyWh": 84.47095489501953,
"exportedActiveEnergyWh": 32.3878288269043
},
{
"id": 2,
"relayState": "CLOSED",
"instantPowerW": -0.25444918870925903,
"importedActiveEnergyWh": 0.31798499822616577,
"exportedActiveEnergyWh": 5361.7060546875
},
...

If anyone also knows the definition of the other fields that would also be helpful.

Cheers!!

@cayossarian
Copy link

cayossarian commented Feb 19, 2024

@BrianLawes The relay state is the physical relay behind the circuit breaker. If you turn a circuit off in the app the relay will be open (the breaker will still be "set" or closed but the circuit will not receive power. They do warn you that if you are doing electrical work to manually open the circuit breaker though and not rely on the relay).

@residualimages
Copy link

Are there any known updates to security or calling on the POSTs for Priority? I can toggle relayState successfully with various POSTs, but the Priority eludes me.

image

image

@patrick-sullivan
Copy link

@residualimages Whatever changed, it's broken in the panel's baked-in Circuits view as well (http://<span_ip_here>/#/circuits). I just tried changing priorities using the drop-downs there there and even that gets a 500 back and fails to actually apply the change.

This is on firmware spanos2/r202405/02.

@cayossarian
Copy link

anyone find endpoints with info about solar? i've got inverters hooked up to my panel but that specific breaker is omitted from the circuits endpoint.

Solar is not currently identified in circuit list but there is a list of all the circuits power, etc. in the panel data. data["branches"][] so you can add one or two circuits together to get the information where you solar is connected.

There is a Home Assistant custom HACS repository that provides solar sensors in this manner. You'll need to add a custom HACS repository as this feature is not currently in the default HACS SPAN integration.https://github.com/cayossarian/span

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment