iaqualink

package module
v0.0.0-...-52e19ee Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 11, 2023 License: MIT Imports: 13 Imported by: 0

README

Go Report Card GoDoc

Go package for talking with iAquaLink/Zodiac pool robots.

This package has been built by reverse-engineering the iAquaLink HTTP protocol using mitmproxy.

This package is very much in development, and there's a good chance that your specific pool robot is not properly supported. If you'd like to help out, set up an mitmproxy and send me your traffic.

Tested With

  • Polaris P965IQ (device type i2d_robot)
  • Zodiac Voyager RE 4400 iQ (device type cyclonext)

Device Types

  • cyclonext; this definitely includes teh Zodiac Voyager.
  • exo; this appears to be a chlorinator of some kind.
  • id2_robot; this definitely includes the Polaris IQ models.

Zodiac API

Base: https://prod.zodiac-io.com

POST /devices/v1/${device}/ota

Returns information on if the device has an update available.

Headers:

  • Authorization: the ID token.

JSON request:

  • deviceType; the device type.
  • forceOTA; either true or false (I've only ever seen it use true).

JSON response:

  • code; some kind of numeric code.
  • message; a human-readable message.

The only values that I'm aware of so far are:

  • 6: Device is already on latest firmware
GET /devices/v2/${device}/shadow

This seems to be identical to GET /devices/v1/${device}/shadow.

POST /devices/v1/${device}/shadow

This appears to let you update the state of the device.

Headers:

  • Authorization: the ID token.

JSON request:

  • state
    • desired
      • equipment
        • ${key}; this is the key returned by the GET version of the endpoint.
          • (Whatever field(s?) you want to change.

For example, for an exo chlorinator, you might set production: 1 or production: 0 to turn it on or off, respectively.

JSON response:

This is practically identical to the response from GET /devices/v2/${device}/shadow.

GET /devices/v2/${device}/features

This returns a list of features that the device supports.

Headers:

  • Authorization: the ID token.
GET /devices/v2/${device}/info

TODO: ???

Headers:

  • Authorization: the ID token.
GET /devices/v2/${device}/site

TODO: ???

Headers:

  • Authorization: the ID token.
GET /devices/v2/${device}/shadow

This appears to return the current state of the device.

Headers:

  • Authorization: the ID token.

JSON response:

  • deviceId; the device identifier.
  • state; the state.
    • reported; the state information as reported by the device.
      • aws; probably AWS-related debug information?
      • debug; debug information?
      • equipment; an object where each key represents a thing of some kind that can be manipulated.
        • ${key}; a thing of some kind. The properties vary based on the device type.
      • (There are other fields)
POST /push/v1/users/unregister-mobile

TODO: ???

POST /users/v1/login

This logs you into the Zodiac API and provides you with a user ID and authentication token. These will be used by the iAquaLink API.

JSON body:

  • apiKey: EOOEMOW4YR6QNB07; this appears to be constant.
  • email; your e-mail address.
  • password; your password.

JSON response (relevant subset):

  • authentication_token; this is the authentication token.
  • email; the same e-mail address.
  • id; this is the user ID.
  • session_id; ???
  • userPoolOAuth; this contains information about OAuth, which is used for newer robot models.
    • AccessToken; the access token.
    • ExpiresIn; when the (access?) token expires.
    • IdToken; the ID token.
    • RefreshToken; the refresh token; this can be used to get a new AccessToken value.
    • TokenType; this seems to always be Bearer, even though it's not technically used correctly.
POST /users/v1/refresh

This uses the user's e-mail addresss and RefreshToken to effectively log in again.

JSON body:

  • email; the user's e-mail address.
  • refresh_token; the user's refreshToken from POST /users/v1/login.

JSON response:

The output is similar to that of POST /users/v1/login. Note, however, that userPoolOAuth.RefreshToken will not be present.

Base: https://prm.iaqualink.net

POST /v2/signout

TODO: ???

Headers:

  • Authorization: the authorization token.

Base: https://r-api.iaqualink.net

GET /devices.json

This lists the devices on your account. You'll need the device ID for any device-related endpoints.

Query parameters:

  • api_key: EOOEMOW4YR6QNB07; this appears to be constant
  • authentication_token; your authentication token
  • user_id; your user ID

Response JSON:

  • An array of objects, each representing a device.
    • device_type; the device type.
    • name; the name given by the user.
    • serial_number; the identifier that will be used to subsequently identify the device.

(There are other fields, but they aren't particularly helpful.)

POST /devices/${device}/execute_read_command.json

This executes a command on a device.

Query parameters:

  • api_key: EOOEMOW4YR6QNB07; this appears to be constant
  • authentication_token; your authentication token
  • user_id; your user ID
  • command; the command
  • params; a query string for the parameters for the command

Commands:

  • /command
    • Parameters:
      • request; the request code
      • timeout; the timeout (iAquaLink default: 800)

Request codes:

  • OAOD; list the schedule
  • OA11; list the status
  • 0A1210; stop / clear error
  • 0A1240; start
  • 0A1300; subtract 30 minutes
  • 0A1301; add 30 minutes
  • 0A1700; lift system, stop
  • 0A1701; lift system, lift
  • 0A1B42; backward
  • 0A1B46; forward
  • 0A1B4C; turn left
  • 0A1B52; turn right

Request code format:

  • 0A <command> [<subcommand>]

Commands:

  • 0D; list the schedule

  • 11; list the status

  • 12; start/stop

    • 10; stop / clear error
    • 40; start
  • 13; add/subtract time

    • 00; subtract 30 minutes
    • 01; add 30 minutes
  • 17; lift system

    • 00; stop
    • 01; start
  • 1B; remote control

    • 42; ASCII "B" - backward
    • 46; ASCII "F" - forward
    • 4C; ASCII "L" - left
    • 52; ASCII "R" - right

Web Socket API

Base: https://prod-socket.zodiac-io.com

/devices

This endpoint appears to be the main endpoint for communication (when supported).

Note that in responses, the metadata object appears to be a mirror of state, but with timestamps where every bottom-level property is.

Action: Subscribe

SEND:

{
    "action": "subscribe",
    "version": 1,
    "namespace": "authorization",
    "payload": {
        "userId": ${userId}
    },
    "service": "Authorization",
    "target": "${device}"
}

RECEIVE:

{
    "service": "Authorization",
    "target": "${device}",
    "namespace": "authorization",
    "payload": {
        "robot": {
            "state": {
                "reported": {
                    "aws": {
                        "status": "connected",
                        "timestamp": 1663228298244,
                        "session_id": "11ae4f85-2f9b-4b82-b726-3528c876ea0e"
                    },
                    "sn": "${device}",
                    "dt": "cyc",
                    "vr": "V21C10",
                    "payloadVer": 1,
                    "eboxData": {
                        "controlBoxSn": "${some-serial-number}",
                        "controlBoxPn": "${some-part-number}",
                        "completeCleanerSn": "${some-serial-number}",
                        "completeCleanerPn": "${some-part-number}",
                        "powerSupplySn": "${some-serial-number}",
                        "motorBlockSn": "${some-serial-number}"
                    },
                    "equipment": {
                        "robot.1": {
                            "mode": 0,
                            "direction": 0,
                            "cycle": 3,
                            "cycleStartTime": 1663140417,
                            "canister": 0,
                            "logger": 0,
                            "firstSmrtFlag": 1,
                            "stepper": 0,
                            "stepperAdjTime": 15,
                            "durations": {
                                "waterTim": 45,
                                "quickTim": 90,
                                "smartTim": 0,
                                "deepTim": 150,
                                "customTim": 75,
                                "firstSmartTim": 150,
                                "scanTim": 30
                            },
                            "customCyc": {
                                "type": 0,
                                "intensity": 0
                            },
                            "errors": {
                                "timestamp": 1663097347,
                                "code":0
                            },
                            "vr": "V21E11",
                            "equipmentId": "ND21015155",
                            "totRunTime": 13410
                        }
                    }
                }
            },
            "metadata": {
                "reported": {
                    "aws": {
                        "status": { "timestamp": 1663228298 },
                        "timestamp": { "timestamp": 1663228298 },
                        "session_id": { "timestamp": 1663228298 }
                    },
                    "sn": { "timestamp": 1662828488 },
                    "dt": { "timestamp": 1662828488 },
                    "vr": { "timestamp": 1662828488 },
                    "payloadVer": { "timestamp": 1662828488 },
                    "eboxData": {
                        "controlBoxSn": { "timestamp": 1662828488 },
                        "controlBoxPn": { "timestamp": 1662828488 },
                        "completeCleanerSn": { "timestamp": 1662828488 },
                        "completeCleanerPn": { "timestamp": 1662828488 },
                        "powerSupplySn": { "timestamp": 1662828488 },
                        "motorBlockSn": { "timestamp":1662828488 }
                    },
                    "equipment": {
                        "robot.1": {
                            "mode": { "timestamp": 1663228321 },
                            "direction": { "timestamp": 1662828488 },
                            "cycle": { "timestamp": 1663228321 },
                            "cycleStartTime": { "timestamp": 1663140419 },
                            "canister": { "timestamp": 1662828488 },
                            "logger": { "timestamp": 1662828488 },
                            "firstSmrtFlag": { "timestamp": 1662828488 },
                            "stepper": { "timestamp": 1662828488 },
                            "stepperAdjTime": { "timestamp":1662828488 },
                            "durations": {
                                "waterTim": { "timestamp": 1662828488 },
                                "quickTim": { "timestamp": 1662828488 },
                                "smartTim": { "timestamp": 1662828488 },
                                "deepTim": { "timestamp": 1662828488 },
                                "customTim": { "timestamp": 1662828488 },
                                "firstSmartTim": { "timestamp": 1662828488 },
                                "scanTim": { "timestamp": 1662828488 }
                            },
                            "customCyc": {
                                "type": { "timestamp": 1662828488 },
                                "intensity": { "timestamp": 1662828488 }
                            },
                            "errors": {
                                "timestamp": { "timestamp": 1663140402 },
                                "code": { "timestamp": 1663140402 }
                            },
                            "vr": { "timestamp": 1662828489 },
                            "equipmentId": { "timestamp": 1662828489 },
                            "totRunTime": { "timestamp": 1663149719 }
                        }
                    }
                }
            },
            "version": 230169,
            "timestamp": 1663228422
        },
        "data": [],
        "ota": {
            "status": "UP_TO_DATE"
        }
    }
}
Action: Start cleaning

As far as I can tell, the clientToken appears to be a random identifier for matching the request with a subsequent event.

SEND:

{
    "action": "setState",
    "version": 1,
    "namespace": "cyclonext",
    "payload": {
        "state": {
            "desired": {
                "equipment": {
                    "robot.1": {
                        "mode": 1
                    }
                }
            }
        },
        "clientToken": "${userId}|AuWGRMyOKDfMvkU4vhK5wj|l8KVqVChow1lV69CcBad0b"
    },
    "service": "StateController",
    "target": "${device}"
}

RECEIVE:

{
    "service": "StateStreamer",
    "target": "${device}",
    "event": "StateReported",
    "version": 1,
    "payload": {
        "state": {
            "desired": {
                "equipment": {
                    "robot.1": {
                        "mode": 1
                    }
                }
            }
        },
        "metadata": {
            "desired": {
                "equipment": {
                    "robot.1": {
                        "mode": { "timestamp": 1663228439 }
                    }
                }
            }
        },
        "version": 230170,
        "timestamp": 1663228439,
        "clientToken": "${userId}|AuWGRMyOKDfMvkU4vhK5wj|l8KVqVChow1lV69CcBad0b"
    }
}
Action: Stop Cleaning

As far as I can tell, the clientToken appears to be a random identifier for matching the request with a subsequent event.

SEND:

{
    "action": "setState",
    "version": 1,
    "namespace": "cyclonext",
    "payload": {
        "state": {
            "desired": {
                "equipment": {
                    "robot.1": {
                        "mode": 0
                    }
                }
            }
        },
        "clientToken": "${userId}|AuWGRMyOKDfMvkU4vhK5wj|CUXkLn7Dyb0OIplLCBVtAQ"
    },
    "service": "StateController",
    "target": "KK2100006435"
}

Other Secret(?) Web Socket API

Base: https://a1zi08qpbrtjyq-ats.iot.us-east-1.amazonaws.com

/mqtt

This endpoint appears to be the main endpoint for communication (when supported).

Query parameters:

  • X-Amz-Algorithm: AWS4-HMAC-SHA256; (?)
  • X-Amz-Credential; this appears to be the value from /login's credentials.AccessKeyId, followed by /, then the date as YYYYMMDD, then the region (from credentials.IdentityId?), followed by /, then iotdata, followed by /, and then aws4_request.
  • X-Amz-Date; this is a date of some kind of the format YYYYMMDDTHHMMSSZ.
  • X-Amz-SignedHeaders: host; (?)
  • X-Amz-Signature; this looks like a SHA sum of some kind (for example: ab4a5bc3e82c19584ca1de54d55d1538a4775f4cf)

I have no idea how to find or create those parameters.

The web socket communication also appears to be in binary format, and I haven't figured out how to decode it yet.

???

Perhaps also on this API is:

  • /aws/things/${device}/shadow/get/accepted (?)

The response is plain text representing hexadecimal data (no spaces).

Response code format:

  • 00 <command> <specific response>
Simple Responses

These commands all seem to respond back with a specific response of 01, which I'm assuming is a basic acknowledgement.

  • Start/clear error response

    0012 01
    
  • Add/subtract time response

    0013 01
    
  • Lift system, lift/stop response

    0017 01
    
  • Left/right/forward/backward response

    001B 01
    
Schedule Response

The result is plain text representing hexadecimal data (no spaces).

Example (every day at 3am):

        Monday
        |    Tuesday
        |    |    Wednesday
        |    |    |   Thursday
        |    |    |    |    Friday
        |    |    |    |    |    Saturday
        |    |    |    |    |    |    Sunday
        |__  |__  |__  |__  |__  |__  |__
     ?? |  \ |  \ |  \ |  \ |  \ |  \ |  \
000D 7F 0300 0300 0300 0300 0300 0300 0300

Days start with Monday and end with Sunday.

Each day is of the form:

Hour (1 byte)
| Minute (1 byte)
| |
AAaa

For example, 0300 is 3am; 030F is 3:15am, 031E is 3:30am, etc.

Note that the iAquaLink app requires that the minute be in 15-minute increments.

Status Response

The result is plain text representing hexadecimal data (no spaces).

Examples (???):

     State
     |  Error code ???
     |  |  Cleaning mode
     |  |  |  Minutes remaining
     |  |  |  |  Uptime (minutes) ???
     |  |  |  |  |      Runtime (minutes) ???
     |  |  |  |  |____  |____
     |\ |\ |\ |\ |    \ |    \ ?????? ??????
0011 04 00 0B 73 09C305 B3FD01 1F4309 0F4570
0011 04 00 0B 00 39CF05 820502 1F4309 0F4570 [9:37pm]
0011 0C 00 0B 00 3ACF05 4E0602 1F4309 0F4570 [9:38pm]
0011 03 00 0B D2 3BCF05 4E0602 1F4309 0F4570 [9:39pm]
0011 01 00 0B D2 0EC305 B3FD01 1F4309 0F4570
0011 04 00 0B 84 AFC605 6F0002 1F4309 0F4570 - Deep - floor and walls (high)
0011 02 00 08 63 B1C605 6F0002 1F4309 0F4570 - Quick - floor only (standard)
0011 02 00 0C 36 B2C605 6F0002 1F4309 0F4570 - Waterline only (standard)
0011 02 00 09 6D B3C605 6F0002 1F4309 0F4570 - Custom - floor (high)
0011 02 00 0A 9F B4C605 6F0002 1F4309 0F4570 - Cusomm - floor and walls (standard)
0011 02 00 0D 40 B5C605 6F0002 1F4309 0F4570 - Custom - waterline (high)
0011 02 00 0B CC BCC605 6F0002 1F4309 0F4570
0011 03 00 0B D2 2ACA05 210302 1F4309 0F4570 - Finished
0011 03 00 0B D2 40CA05 210302 1F4309 0F4570
0011 03 00 0B D2 44CA05 210302 1F4309 0F4570 - Finished [12:07am]
0011 03 00 0B D2 45CA05 210302 1F4309 0F4570 - Finished [12:08am]
0011 03 00 0B D2 46CA05 210302 1F4309 0F4570 - Finished [12:09am]
0011 0D 08 0B D2 B3D105 180702 1F4309 0F4570 - Error - out of water [8:19am]
0011 03 00 0B D2 8DD305 E40702 1F4309 0F4570 [4:21pm]
0011 02 00 0B D1 8FD305 E40702 1F4309 000000 [4:23pm]
0011 02 00 0B D0 90D305 E40702 1F4309 0F4570 [4:24pm]
0011 03 00 0B D2 2FEC05 F71202 1F4309 0F4570 - Finished [2:59am]
0011 02 00 0B D1 30EC05 F71202 1F4309 0F4570 - Started [3:00am]
0011 0D 08 0B D2 31EC05 A51302 1F4309 0F4570 - Error - out of water [3:01am]
0011 0E 08 0B D2 3BEC05 A51302 1F4309 0F4570 - Error - ??? [3:11am]
0011 0E 05 0B D2 8D4707 0CE202 1F4309 0F4570 - Error - drive motor consumption right
0011 0E 0A 0B D2 885507 25E302 1F4309 000000 - Error - communication
0011 04 00 0B 03 87DC07 D80F00 1F4309 0F4580 [after replacing the motor block]
0011 0C 00 0B 00 8CDC07 D80F00 1F4309 0F4580 [as it's finishing up]
0011 03 00 0B D2 8CDC07 A21000 1F4309 0F4580 [after it finishes]

State:

  • 01; stopped (manually?)
  • 02; running
  • 03; finished (on its own?)
  • 04; running
  • 0A; lift system
  • 0B; remote control
  • OC; finishing???
  • 0D; error - first 10 minutes???
  • OE; error - after 10 minutes???

I've seen it transition from 02 to 04 10 minutes into a scheduled cleaning and 10 minutes into a manual cleaning.

I've seen it transition from OD to OE 10 minutes into a scheduled cleaning when the robot was out of the water.

I've seen it transition from 04 to 0C to 03 over the span of 3 minutes.

The runtime only appears to update after a cleaning cycle completes.

Error code ???:

  • 00; no error
  • 04; pump motor consumption
  • 05; drive motor consumption right
  • 08; out of water
  • 0a; communication

Cleaning mode (& 0x0f):

  • 0x8; floor (standard)
  • 0x9; floor (high) ["custom" in the app]
  • 0xA; floor and walls (standard) ["custom" in the app]
  • 0xB; floor and walls (high)
  • 0xC; waterline (standard)
  • 0xD; waterline (high) ["custom" in the app]

Cleaning mode (& 0x10 >> 8):

  • 0x0; no notes
  • 0x1; the canister is full

Development

Traffic Capture with mitmproxy

If you'd like to help get your particular pool robot working (or help with a subset of functionality that isn't properly supported), set up an mitmproxy and configure your phone's wifi network to use the proxy. This will allow you to capture (and decrypt) all of the traffic that goes to and from the Zodiac/iAquaLink APIs.

For information about mitmproxy, see: https://mitmproxy.org/

When using mitmproxy, please only perform iAquaLink operations and then immediately remove the proxy settings. I don't want you to accidentally capture your passwords and credentials from other pieces of software.

If possible, only use a spare phone (or an emulator) that only has iAquaLink installed.

The ideal workflow is the following:

  1. Log out of your iAquaLink account.
  2. Start mitmproxy.
  3. Configure your phone's wifi to use the proxy.
  4. Open iAquaLink.
  5. Login.
  6. Perform a single action.
  7. Stop mitmproxy and save the results.
  8. Configure your phone's wifi to not use the proxy anymore.

This way, the traffic captured only has the login operation and a single operation. If you're going to share this capture with someone, please change your password after completing it (or change it before you start and then change it back when you're done).

If you perfom multiple operations, please write down which operations you performed in the order that you performed them.

Documentation

Index

Constants

View Source
const (
	DeviceTypeCycloneXT = "cyclonext"
	DeviceTypeI2DRobot  = "i2d_robot"
)

DeviceType constants.

View Source
const (
	FeatureModeInfo        = "mode_info"
	FeaturepresetDeepUltra = "preset_deep_ultra"
	FeatureQuickClean      = "quick_clean"
	FeatureStartStop       = "start_stop"
	FeatureStatus          = "status"
	FeatureTimer           = "timer"
)

Feature constants.

View Source
const APIKey = "EOOEMOW4YR6QNB07"

APIKey is the API key to use when talking to the APIs. This appears to be a constant.

View Source
const IAquaLinkAPIBase = "https://r-api.iaqualink.net"
View Source
const WebSocketAPIBase = "https://prod-socket.zodiac-io.com"
View Source
const ZodiacAPIBase = "https://prod.zodiac-io.com"

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	IAquaLinkAPIBase string // The iAquaLink API base.  If empty, this will default to `IAquaLinkAPIBase`.
	WebSocketAPIBase string // The Web Socket API base.  If empty, this will default to `WebSocketAPIBase`.
	ZodiacAPIBase    string // The Zodiac API base.  If empty, this will default to `ZodiacAPIBase`.

	APIKey string // The API key.  If empty, this will default to `APIKey`.

	AuthenticationToken string // The API token.
	IDToken             string // The OAuth ID token.
	UserID              string // The user ID.

	Client *http.Client // The HTTP client.
}

Client is the iAquaLink client.

func (*Client) DeviceExecuteReadCommand

func (c *Client) DeviceExecuteReadCommand(deviceID string, inputValues url.Values) (*DeviceExecuteReadCommandOutput, error)

DeviceExecuteReadCommand executes a command.

func (*Client) DeviceFeatures

func (c *Client) DeviceFeatures(deviceID string) (*DeviceFeaturesOutput, error)

DeviceFeatures lists the device features.

func (*Client) DeviceOTA

func (c *Client) DeviceOTA(deviceID string) (*DeviceOTAOutput, error)

DeviceOTA lists the device features.

func (*Client) DeviceSite

func (c *Client) DeviceSite(deviceID string) (*DeviceSiteOutput, error)

DeviceSite lists the device features.

func (*Client) DeviceWebSocket

func (c *Client) DeviceWebSocket(deviceID string, actions ...string) (map[string]string, error)

DeviceWebSocket performs an action over the web socket.

func (*Client) ListDevices

func (c *Client) ListDevices() (ListDevicesOutput, error)

ListDevices lists the devices.

func (*Client) Login

func (c *Client) Login(username, password string) (*LoginOutput, error)

Login with the given username and password.

func (*Client) Raw

func (c *Client) Raw(method string, base string, path string, parameters url.Values, input interface{}) ([]byte, error)

Raw performs a raw request using the API.

type DeviceExecuteReadCommandOutput

type DeviceExecuteReadCommandOutput struct {
	Command struct {
		Request  string `json:"request"`
		Response string `json:"response"`
	} `json:"command"`
	RequestID string `json:"requestID"`
}

DeviceExecuteReadCommandOutput is the output of executing a command.

type DeviceFeaturesOutput

type DeviceFeaturesOutput struct {
	DeviceID string   `json:"deviceId"`
	Features []string `json:"features"`
	ID       string   `json:"id"`
	Model    string   `json:"model"`
}

DeviceFeaturesOutput is the output of listing the features.

type DeviceOTAOutput

type DeviceOTAOutput struct {
	DeviceID string `json:"deviceId"`
	Status   string `json:"status"`
}

DeviceOTAOutput is the output of querying for an OTA update.

type DeviceSiteOutput

type DeviceSiteOutput struct {
	DaylightSavings int    `json:"daylight_savings"` // 1 if daylight saving time; 0 otherwise.
	TimeZone        string `json:"time_zone"`        // In the format of "[+-][0-9][0-9]:[0-9][0-9]".
}

DeviceSiteOutput is the output of listing the features.

type DeviceWebSocketInput

type DeviceWebSocketInput struct {
	Action    string      `json:"action"`
	Version   int         `json:"version"`
	Namespace string      `json:"namespace"`
	Payload   interface{} `json:"payload"`
	Service   string      `json:"service"`
	Target    string      `json:"target"`
}

type DeviceWebSocketInputSetStatePayload

type DeviceWebSocketInputSetStatePayload struct {
	State       map[string]interface{} `json:"state"`
	ClientToken string                 `json:"clientToken"`
}

type DeviceWebSocketInputSubscribePayload

type DeviceWebSocketInputSubscribePayload struct {
	UserID int `json:"userId"`
}

type DeviceWebSocketOutput

type DeviceWebSocketOutput struct {
	Event     string           `json:"event,omitempty"`
	Namespace string           `json:"namespace,omitempty"`
	Payload   *json.RawMessage `json:"payload"`
	Service   string           `json:"service"`
	Target    string           `json:"target"`
	Version   int              `json:"version,omitempty"`
}

type DeviceWebSocketOutputSubscribePayload

type DeviceWebSocketOutputSubscribePayload struct {
	Robot struct {
		State struct {
			Reported struct {
				// TODO: "aws"
				// TODO: "sn"
				// TODO: "dt"
				// TODO: "vr"
				PayloadVersion int                    `json:"payloadVer"`
				EboxData       map[string]interface{} `json:"eboxData"`
				Equipment      map[string]struct {
					Mode int `json:"mode"`
				} `json:"equipment"`
			} `json:"reported"`
		} `json:"state"`
	} `json:"robot"`
	// TODO: "data"
	OTA struct {
		Status string `json:"status"`
	} `json:"ota"`
}

type ListDevicesOutput

type ListDevicesOutput []struct {
	ID                    int        `json:"id"`
	SerialNumber          string     `json:"serial_number"`
	CreatedAt             time.Time  `json:"created_at"`
	UpdatedDat            time.Time  `json:"updated_at"`
	Name                  string     `json:"name"`
	DeviceType            string     `json:"device_type"`
	OwnerID               int        `json:"owner_id"`
	Updating              bool       `json:"updating"`
	FirmwareVersion       *string    `json:"firmware_version"`
	TargetFirmwareVersion *string    `json:"target_firmware_version"`
	UpdateFirmwareStartAt *time.Time `json:"update_firmware_start_at"`
	LastActivityAt        *time.Time `json:"last_activity_at"`
}

ListDevicesOutput is the output of listing the devices.

type LoginInput

type LoginInput struct {
	APIKey   string `json:"apiKey"`
	Email    string `json:"email"`
	Password string `json:"password"`
}

LoginInput is the login input.

type LoginOutput

type LoginOutput struct {
	Address             string `json:"address"`
	Address1            string `json:"address_1"`
	Address2            string `json:"address_2"`
	AuthenticationToken string `json:"authentication_token"`
	City                string `json:"city"`
	CognitoPool         struct {
		AppClientID string `json:"appClientId"`
		PoolID      string `json:"poolId"`
		Region      string `json:"region"`
	} `json:"cognitoPool"`
	Country     string `json:"country"`
	CreatedAt   string `json:"created_at"` // Should be `time.Time`, but oh well.
	Credentials struct {
		AccessKeyID  string `json:"AccessKeyId"`
		Expiration   string `json:"Expiration"` // Should be `time.Time`, but oh well.
		IdentityID   string `json:"IdentityId"`
		SecretKey    string `json:"SecretKey"`
		SessionToken string `json:"SessionToken"`
	} `json:"credentials"`
	Email         string         `json:"email"`
	FirstName     string         `json:"first_name"`
	ID            StringOrNumber `json:"id"`
	LastName      string         `json:"last_name"`
	OptIn1        string         `json:"opt_in_1"`
	OptIn2        string         `json:"opt_in_2"`
	Phone         string         `json:"phone"`
	PostalCode    string         `json:"postal_code"`
	Role          string         `json:"role"`
	SessionID     string         `json:"session_id"`
	State         string         `json:"state"`
	TimeZone      string         `json:"time_zone"`
	UpdatedAt     string         `json:"updated_at"` // Should be `time.Time`, but oh well.
	UserPoolOAuth struct {
		AccessToken  string `json:"AccessToken"`
		ExpiresIn    int    `json:"ExpiresIn"`
		IDToken      string `json:"IdToken"`
		RefreshToken string `json:"RefreshToken"`
		TokenType    string `json:"TokenType"`
	} `json:"userPoolOAuth"`
	Username string `json:"username"`
}

LoginOutput is the login output.

type StringOrNumber

type StringOrNumber struct {
	// contains filtered or unexported fields
}

StringOrNumber represents either a string or a number. The underlying representation is a string, but it'll parse in either direction.

func (StringOrNumber) MarshalJSON

func (s StringOrNumber) MarshalJSON() ([]byte, error)

func (StringOrNumber) String

func (s StringOrNumber) String() string

func (*StringOrNumber) UnmarshalJSON

func (s *StringOrNumber) UnmarshalJSON(contents []byte) error

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL