Reference

For convenience, all classes documented here are re-exported in the flat pishock namespace.

Basic Data structures

class pishock.zap.core.Shocker

Base class for HTTPShocker and SerialShocker.

Applications which only need access to shock(), vibrate(), beep(), and info() (with BasicShockerInfo only) can swap out a HTTPShocker for a SerialShocker (with only initialization changing) to support both APIs.

class pishock.zap.core.BasicShockerInfo(name: str, client_id: int, shocker_id: int, is_paused: bool)

Basic information about a shocker.

Used by PiShockAPI.get_shockers() and SerialShocker.info(). Calling HTTPShocker.info() returns a httpapi.DetailedShockerInfo instance instead.

name

The name of this shocker in the web interface (or an autogenerated name for serial shockers).

Type:

str

client_id

The ID of the PiShock this shocker belongs to.

Type:

int

shocker_id

The ID of this shocker.

Type:

int

is_paused

Whether the shocker is currently paused.

Type:

bool

API access

class pishock.zap.httpapi.DetailedShockerInfo(name: str, client_id: int, shocker_id: int, is_paused: bool, max_intensity: int, max_duration: int)

Detailed information about a shocker.

Used by HTTPShocker.info(). Calling PiShockAPI.get_shockers() or pishock.zap.serialapi.SerialShocker.info() returns a pishock.zap.core.BasicShockerInfo instance instead.

This class extends pishock.zap.core.BasicShockerInfo with the following attributes:

max_intensity

The maximum intensity (0-100) the shocker can be set to.

Type:

int

max_duration

The maximum duration (0-15) the shocker can be set to.

Type:

int

class pishock.zap.httpapi.PiShockAPI(username: str, api_key: str)

Base entry point for the PiShock API.

Parameters:
get_shockers(client_id: int) list[BasicShockerInfo]

Get a list of all shockers for the given client (PiShock) ID.

Raises:
request(endpoint: str, params: dict[str, Any]) Response

Make a raw request to the API.

All requests are POST requests with params passed as JSON, because the API seems to be like that.

Normally, you should not need to use this method directly.

Raises:

HTTPError – If the API returns an invalid HTTP status.

shocker(sharecode: str, log_name: str = 'Python-PiShock', name: str | None = None) HTTPShocker

Get a HTTPShocker instance for the given share code.

This is the main entry point for almost all remaining API usages.

Parameters:
  • sharecode – The share code generated via the web interface.

  • log_name – How the shocker should be named in the logs on the website.

  • name – Used when converting the HTTPShocker to a string, defaults to sharecode.

verify_credentials() bool

Check if the API credentials are valid.

Returns:

True on success, False on authentication failure.

Raises:

HTTPError – If the API returns an invalid HTTP status.

class pishock.zap.httpapi.HTTPShocker(api: PiShockAPI, sharecode: str, name: str | None, log_name: str)

Represents a single shocker / share code using the HTTP API.

Normally, there should be no need to instanciate this manually, use PiShockAPI.shocker() instead.

beep(duration: int | float) None

Send a beep with the given duration (0-15).

Durations can also be floats between 0.1 and 1.5 (inclusive), with the following caveats:

  • The duration is rounded down to the nearest 0.1 seconds.

  • On old Plus models, e.g. 0.3 is interpreted as 3s instead of 300ms.

  • This is an experimental and undocumented feature of the API, so it might break at any time.

Raises:
  • ValueErrorduration is out of range.

  • APIError – Any of the APIError subclasses in this module, refer to their documenation for details.

info() DetailedShockerInfo

Get detailed information about the shocker.

Raises:
pause(pause: bool) None

Pause/unpause the shocker.

Parameters:

pause – Whether to pause or unpause the shocker.

Raises:
shock(*, duration: int | float, intensity: int) None

Send a shock with the given duration (0-15) and intensity (0-100).

Durations can also be floats between 0.1 and 1.5 (inclusive), with the following caveats:

  • The duration is rounded down to the nearest 0.1 seconds.

  • On old Plus models, e.g. 0.3 is interpreted as 3s instead of 300ms.

  • This is an experimental and undocumented feature of the API, so it might break at any time.

Raises:
  • ValueErrorduration or intensity are out of range.

  • APIError – Any of the APIError subclasses in this module, refer to their documenation for details.

vibrate(*, duration: int | float, intensity: int) None

Send a vibration with the given duration (0-15) and intensity (0-100).

Durations can also be floats between 0.1 and 1.5 (inclusive), with the following caveats:

  • The duration is rounded down to the nearest 0.1 seconds.

  • On old Plus models, e.g. 0.3 is interpreted as 3s instead of 300ms.

  • This is an experimental and undocumented feature of the API, so it might break at any time.

Raises:
  • ValueErrorduration or intensity are out of range.

  • APIError – Any of the APIError subclasses in this module, refer to their documenation for details.

API Errors

exception pishock.zap.httpapi.APIError

Base class for all errors returned by the API.

exception pishock.zap.httpapi.HTTPError(requests_error: HTTPError)

Invalid HTTP status from the API.

exception pishock.zap.httpapi.UnknownError

Unknown message returned from the API.

exception pishock.zap.httpapi.OperationNotAllowedError

API returned: <Operation> not allowed.

Used as a base class for ShockNotAllowedError, VibrateNotAllowedError and BeepNotAllowedError.

exception pishock.zap.httpapi.BeepNotAllowedError

API returned: Beep not allowed.

exception pishock.zap.httpapi.VibrateNotAllowedError

API returned: Vibrate not allowed.

exception pishock.zap.httpapi.ShockNotAllowedError

API returned: Shock not allowed.

exception pishock.zap.httpapi.NotAuthorizedError

API returned: Not Authorized.

exception pishock.zap.httpapi.ShockerPausedError

API returned: Shocker is Paused or does not exist. Unpause to send command.

exception pishock.zap.httpapi.DeviceInUseError

API returned: Device in Use.

exception pishock.zap.httpapi.DeviceNotConnectedError

API returned: Device currently not connected.

exception pishock.zap.httpapi.ShareCodeAlreadyUsedError

API returned: This share code has already been used by somebody else.

exception pishock.zap.httpapi.ShareCodeNotFoundError

API returned: This code doesn’t exist.

Serial

High-level API

The pishock.zap.serialapi.SerialShocker shares a common pishock.zap.core.Shocker base with pishock.zap.httpapi.HTTPShocker. It can thus act as a drop-in replacement for using the HTTP API, and is recommended for most usage.

class pishock.zap.serialapi.SerialShocker(api: SerialAPI, shocker_id: int)

Represents a single shocker accessed via serial port.

Normally, there should be no need to instanciate this manually, use SerialAPI.shocker() instead.

Parameters:
  • api – The SerialAPI instance to use.

  • shocker_id – The ID of the shocker to operate.

Raises:

ShockerNotFoundError – The given shocker_id was not found.

beep(duration: int | float) None

Send a beep with the given duration (seconds, >0).

Durations can also be floats for fractional seconds.

Raises:

ValueErrorduration is out of range.

end() None

End the currently running operation.

info() BasicShockerInfo

Get information about the shocker.

shock(*, duration: int | float, intensity: int) None

Send a shock with the given duration (seconds, >0) and intensity (0-100).

Durations can also be floats for fractional seconds.

Raises:

ValueErrorduration or intensity are out of range.

vibrate(*, duration: int | float, intensity: int) None

Send a vibration with the given duration (seconds, >0) and intensity (0-100).

Durations can also be floats for fractional seconds.

Raises:

ValueErrorduration or intensity are out of range.

Low-level API

The low-level pishock.zap.serialapi.SerialAPI class can be used to send raw serial commands to the PiShock.

class pishock.zap.serialapi.SerialAutodetectError

Raised if there are multiple or no PiShocks found via port autodetection.

class pishock.zap.serialapi.ShockerNotFoundError

Raised if a shocker ID is not found.

class pishock.zap.serialapi.SerialOperation(*values)

The operation to perform for SerialAPI.operate().

SHOCK

Send a shock to the shocker.

VIBRATE

Send a vibration to the shocker

BEEP

Send a beep to the shocker.

END

End the current operation.

class pishock.zap.serialapi.SerialAPI(port: str | None)

Low-level access to PiShock serial functionality.

Parameters:

port – Serial port to use, e.g. COM2 or /dev/ttyUSB0. If None, auto-detection is attempted.

Raises:

SerialAutodetectError – No port was given, and either no PiShock was found, or multiple PiShocks were found.

add_network(ssid: str, password: str) None

Add a new network to the PiShock config and reboot.

decode_info(line: bytes) dict[str, Any]

Decode a TERMINALINFO: line.

Normally, you should not need to call this manually, use wait_info() or info() instead.

info(*, timeout: int | None = 20, debug: bool = False) dict[str, Any]

Get device info.

The exact contents of the returned dict might depend on the PiShock firmware version. At the time of writing, it looks like this (some data redacted):

{
    'version': '3.1.1.231119.1556',
    'type': 4,  # 3 = Next, 4 = Lite
    'connected': False,
    'clientId': 621,
    'wifi': 'redacted-wifi-ssid',
    'server': 'eu1.pishock.com',
    'macAddress': '0C:B8:15:AB:CD:EF',
    'shockers': [
        {'id': 420, 'type': 1, 'paused': False},  # 0 = Petrainer, 1 = SmallOne
    ],
    'networks': [
        {'ssid': 'redacted-wifi-ssid', 'password': 'hunter2'},
        {'ssid': 'PiShock', 'password': 'Zappy454'}
    ],
    'otk': 'e71d7b27-dc38-4774-bafc-c427757f0134',
    'claimed': True,
    'isDev': False,
    'publisher': False,
    'polled': True,
    'subscriber': True,
    'publicIp': '203.0.113.69',
    'internet': True,
    'ownerId': 6969
}
Parameters:
  • timeout – How many seconds or serial lines to wait for the info response.

  • debug – Print the raw serial output to stdout while waiting.

Raises:

TimeoutError – No info was received within the given timeout.

monitor() Iterator[bytes]

Monitor serial output.

operate(shocker_id: int, operation: SerialOperation, duration: int | float, intensity: int | None = None) None

Operate a shocker.

Note that the firmware will silently ignore any commands for a non-existing shocker ID.

You should not need to use this directly, use shocker() to get access to the higher-level SerialShocker instead.

remove_network(ssid: str) None

Remove a network from the PiShock config.

restart() None

Restart the PiShock.

shocker(shocker_id: int) SerialShocker

Get a SerialShocker instance for the given shocker code.

This is the main entry point for operating a shocker via serial.

Parameters:

shocker_id – The shocker ID, as displayed under the cogwheels on the PiShock website, or available via pishock.zap.httpapi.HTTPShocker.info() or SerialAPI.info().

try_connect(ssid: str, password: str) None

Temporarily try connecting to the given network.

wait_info(timeout: int | None = 20, debug: bool = False) dict[str, Any]

Wait for device info without sending an info command.

This will block until the next TERMINALINFO: line is received. You should normally call info() instead. This is useful after sending a command that is expected to return info on its own, e.g. add_network().

Parameters:
  • timeout – How many seconds or serial lines to wait for the info response.

  • debug – Print the raw serial output to stdout while waiting.

Raises:

TimeoutError – No info was received within the given timeout.