Module pyempatica.empaticae4

Expand source code
import socket
import threading
import subprocess
import time
import pickle


class EmpaticaServerConnectError(Exception):
    """
    Custom exception for when the socket fails to connect to the Empatica Server.
    """
    pass


class EmpaticaCommandError(Exception):
    """
    Custom exception for when an Empatica response is an error message.
    """
    pass


class EmpaticaDataError(Exception):
    """
    Custom exception for when there is an error when parsing a data message.
    """
    pass


class EmpaticaDataStreams:
    """
    Applicable data streams that can be received from the Empatica server.
    """
    ACC = b'acc'
    BAT = b'bat'
    BVP = b'bvp'
    GSR = b'gsr'
    IBI = b'ibi'
    TAG = b'tag'
    TMP = b'tmp'
    ALL_STREAMS = [b'acc', b'bat', b'bvp', b'gsr', b'ibi', b'tag', b'tmp']


def start_e4_server(exe_path):
    """
    Starts the Empatica Streaming Server.
    :param exe_path: str: full path to Empatica Streaming Server executable
    :return: None.
    """
    subprocess.Popen(exe_path)


class EmpaticaClient:
    """
    Client object to handle the socket connection to the Empatica Server.
    """

    def __init__(self):
        """
        Initializes the socket connection and starts the data reception thread.
        """
        self.waiting = False
        try:
            self.socket_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket_conn.connect(('127.0.0.1', 28000))
        except ConnectionError as e:
            raise EmpaticaServerConnectError(e)
        self.device = None
        self.device_list = []
        self.reading_thread = None
        self.reading = True
        self.start_receive_thread()
        self.readings = 0

    def close(self):
        """
        Stops the reading thread and closes the socket.
        :return: None.
        """
        self.stop_reading_thread()
        self.socket_conn.close()

    def send(self, packet):
        """
        Blocking method to send a packet to the Empatica Server.
        :param packet: str command
        :return: None.
        """
        self.socket_conn.send(packet)

    def recv(self):
        """
        Blocking method to receive a packet from the Empatica Server.
        :return: bytes-like.
        """
        return self.socket_conn.recv(4096)

    def start_receive_thread(self):
        """
        Starts the receiving thread to handle responses from Empatica Server.
        :return: None.
        """
        self.reading = True
        self.reading_thread = threading.Thread(target=self.handle_reading_receive)
        self.reading_thread.start()

    # https://developer.empatica.com/windows-streaming-server-commands.html
    def handle_reading_receive(self):
        """
        Parses and handles packets received from the Empatica Server.
        :return: None.
        """
        while self.reading:
            try:
                return_bytes = self.socket_conn.recv(4096)
                return_bytes = return_bytes.split()
                if return_bytes[0] == b'R':
                    if b'ERR' in return_bytes:
                        self.handle_error_code(return_bytes)
                    elif b'connection' in return_bytes:
                        self.handle_error_code(return_bytes)
                    elif b'device' in return_bytes:
                        self.handle_error_code(return_bytes)
                    elif b'device_list' in return_bytes:
                        for i in range(4, len(return_bytes), 2):
                            if return_bytes[i + 1] == b'Empatica_E4':
                                self.device_list.append(return_bytes[i])
                    elif b'device_connect' in return_bytes:
                        self.device.connected = True
                        self.device.start_window_timer()
                    elif b'device_subscribe' in return_bytes:
                        self.device.subscribed_streams[return_bytes[2].decode("utf-8")] = \
                            not self.device.subscribed_streams.get(return_bytes[2].decode("utf-8"))
                elif return_bytes[0][0:2] == b'E4':
                    self.handle_data_stream(return_bytes)
            except ConnectionAbortedError:
                pass
            except ConnectionResetError:
                pass
            except ConnectionError:
                pass

    def stop_reading_thread(self):
        """
        Sets the reading thread variable to False to stop the reading thread.
        :return: None.
        """
        self.reading = False

    @staticmethod
    def handle_error_code(error):
        """
        Parses error code for formatting in Exception message.
        :param error: bytes-like error message.
        :return: None.
        """
        message = ""
        for err in error:
            message = message + err.decode("utf-8") + " "
        raise EmpaticaCommandError(message)

    def handle_data_stream(self, data):
        """
        Parses and saves the data received from the Empatica Server.
        :param data: bytes-like packet.
        :return: None.
        """
        try:
            self.readings += 1
            data_type = data[0][3:]
            if data_type == b'Acc':
                self.device.acc_3d.append(float(data[2]))
                self.device.acc_3d.append(float(data[3]))
                self.device.acc_3d.append(float(data[4]))
                self.device.acc_x.append(float(data[2]))
                self.device.acc_y.append(float(data[3]))
                self.device.acc_z.append(float(data[4]))
                self.device.acc_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Bvp':
                self.device.bvp.append(float(data[2]))
                self.device.bvp_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Gsr':
                self.device.gsr.append(float(data[2]))
                self.device.gsr_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Temperature':
                self.device.tmp.append(float(data[2]))
                self.device.tmp_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Ibi':
                self.device.ibi.append(float(data[2]))
                self.device.ibi_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Hr':
                self.device.hr.append(float(data[2]))
                self.device.hr_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Battery':
                self.device.bat.append(float(data[2]))
                self.device.bat_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Tag':
                self.device.tag.append(float(data[2]))
                self.device.tag_timestamps(float(data[1]))
                pass
            else:
                raise EmpaticaDataError(data)
        except:
            raise EmpaticaDataError(data)

    def list_connected_devices(self):
        """
        Sends the list connected devices command to get the devices auto-connected over BLE.
        :return: None
        """
        self.socket_conn.send(b'device_list\r\n')


class EmpaticaE4:
    """
    Class to wrap the client socket connection and configure the data streams.
    """

    def __init__(self, device_name, window_size=None):
        """
        Initializes the socket connection and connects the Empatica E4 specified.
        :param device_name: str: The Empatica E4 to connect to
        """
        self.window_size = window_size
        self.window_thread = threading.Thread(target=self.timer_thread)
        self.client = EmpaticaClient()
        self.connected = False
        self.connect(device_name)
        while not self.connected:
            pass
        self.suspend_streaming()
        self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps = [], [], [], [], []
        self.bvp, self.bvp_timestamps = [], []
        self.gsr, self.gsr_timestamps = [], []
        self.tmp, self.tmp_timestamps = [], []
        self.tag, self.tag_timestamps = [], []
        self.ibi, self.ibi_timestamps = [], []
        self.bat, self.bat_timestamps = [], []
        self.hr, self.hr_timestamps = [], []
        self.windowed_readings = []
        self.subscribed_streams = {
            "acc": False,
            "bvp": False,
            "gsr": False,
            "tmp": False,
            "tag": False,
            "ibi": False,
            "bat": False
        }

    def start_window_timer(self):
        if self.window_size:
            self.window_thread.start()

    def timer_thread(self):
        if self.window_size:
            while self.connected:
                time.sleep(self.window_size)
                self.split_window()

    def split_window(self):
        # Save all the readings to our window
        self.windowed_readings.append(
            (self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps,
             self.bvp, self.bvp_timestamps,
             self.gsr, self.gsr_timestamps,
             self.tmp, self.tmp_timestamps,
             self.tag, self.tag_timestamps,
             self.ibi, self.ibi_timestamps,
             self.bat, self.bat_timestamps,
             self.hr, self.hr_timestamps)
        )
        # Clear all readings collected so far
        self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps = [], [], [], [], []
        self.bvp, self.bvp_timestamps = [], []
        self.gsr, self.gsr_timestamps = [], []
        self.tmp, self.tmp_timestamps = [], []
        self.tag, self.tag_timestamps = [], []
        self.ibi, self.ibi_timestamps = [], []
        self.bat, self.bat_timestamps = [], []
        self.hr, self.hr_timestamps = [], []

    def close(self):
        """
        Closes the socket connection.
        :return: None.
        """
        self.connected = False
        self.client.close()

    def send(self, command):
        """
        Blocking method to send data to Empatica Server.
        :param command: bytes-like: data to send
        :return: None.
        """
        self.client.send(command)

    def receive(self):
        """
        Blocking method to receive data from Empatica Server.
        :return: bytes-like: packet received.
        """
        return self.client.recv()

    def connect(self, device_name):
        """
        Sends the connect command packet to the Empatica Server.
        :param device_name: bytes-like: Empatica E4 to connect to
        :return: None.
        """
        command = b'device_connect ' + device_name + b'\r\n'
        self.send(command)
        self.client.device = self

    def disconnect(self):
        """
        Sends the disconnect command packet to the Empatica Server.
        :return: None.
        """
        command = b'device_disconnect\r\n'
        self.send(command)
        self.client.stop_reading_thread()

    def save_readings(self, filename):
        """
        Saves the readings currently collected to the specified filepath.
        :param filename: str: full path to file to save to
        :return: None.
        """
        if self.windowed_readings:
            with open(filename, "wb") as file:
                pickle.dump(self.windowed_readings, file)
        else:
            with open(filename, "w") as file:
                for reading in self.acc_3d:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.acc_x:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.acc_y:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.acc_z:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.acc_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.gsr:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.gsr_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.bvp:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.bvp_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.tmp:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.tmp_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.hr:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.hr_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.ibi:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.ibi_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.bat:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.bat_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")

    def clear_readings(self):
        """
        Clears the readings collected.
        :return: None.
        """
        self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps = [], [], [], [], []
        self.bvp, self.bvp_timestamps = [], []
        self.gsr, self.gsr_timestamps = [], []
        self.tmp, self.tmp_timestamps = [], []
        self.tag, self.tag_timestamps = [], []
        self.ibi, self.ibi_timestamps = [], []
        self.bat, self.bat_timestamps = [], []
        self.hr, self.hr_timestamps = [], []

    def subscribe_to_stream(self, stream):
        """
        Subscribes the socket connection to a data stream, blocks until the Empatica Server responds.
        :param stream: bytes-like: data to stream.
        :return: None.
        """
        command = b'device_subscribe ' + stream + b' ON\r\n'
        self.send(command)
        while not self.subscribed_streams.get(stream.decode("utf-8")):
            pass

    def unsubscribe_from_stream(self, stream):
        """
        Unsubscribes the socket connection from a data stream, blocks until the Empatica Server responds.
        :param stream: bytes-like: data to stop streaming.
        :return: None.
        """
        command = b'device_subscribe ' + stream + b' OFF\r\n'
        self.send(command)
        while self.subscribed_streams.get(stream.decode("utf-8")):
            pass

    def suspend_streaming(self):
        """
        Stops the data streaming from the Empatica Server for the Empatica E4.
        :return: None.
        """
        command = b'pause ON\r\n'
        self.send(command)

    def start_streaming(self):
        """
        Starts the data streaming from the Empatica Server for the Empatica E4.
        :return: None.
        """
        command = b'pause OFF\r\n'
        self.send(command)

Functions

def start_e4_server(exe_path)

Starts the Empatica Streaming Server. :param exe_path: str: full path to Empatica Streaming Server executable :return: None.

Expand source code
def start_e4_server(exe_path):
    """
    Starts the Empatica Streaming Server.
    :param exe_path: str: full path to Empatica Streaming Server executable
    :return: None.
    """
    subprocess.Popen(exe_path)

Classes

class EmpaticaClient

Client object to handle the socket connection to the Empatica Server.

Initializes the socket connection and starts the data reception thread.

Expand source code
class EmpaticaClient:
    """
    Client object to handle the socket connection to the Empatica Server.
    """

    def __init__(self):
        """
        Initializes the socket connection and starts the data reception thread.
        """
        self.waiting = False
        try:
            self.socket_conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket_conn.connect(('127.0.0.1', 28000))
        except ConnectionError as e:
            raise EmpaticaServerConnectError(e)
        self.device = None
        self.device_list = []
        self.reading_thread = None
        self.reading = True
        self.start_receive_thread()
        self.readings = 0

    def close(self):
        """
        Stops the reading thread and closes the socket.
        :return: None.
        """
        self.stop_reading_thread()
        self.socket_conn.close()

    def send(self, packet):
        """
        Blocking method to send a packet to the Empatica Server.
        :param packet: str command
        :return: None.
        """
        self.socket_conn.send(packet)

    def recv(self):
        """
        Blocking method to receive a packet from the Empatica Server.
        :return: bytes-like.
        """
        return self.socket_conn.recv(4096)

    def start_receive_thread(self):
        """
        Starts the receiving thread to handle responses from Empatica Server.
        :return: None.
        """
        self.reading = True
        self.reading_thread = threading.Thread(target=self.handle_reading_receive)
        self.reading_thread.start()

    # https://developer.empatica.com/windows-streaming-server-commands.html
    def handle_reading_receive(self):
        """
        Parses and handles packets received from the Empatica Server.
        :return: None.
        """
        while self.reading:
            try:
                return_bytes = self.socket_conn.recv(4096)
                return_bytes = return_bytes.split()
                if return_bytes[0] == b'R':
                    if b'ERR' in return_bytes:
                        self.handle_error_code(return_bytes)
                    elif b'connection' in return_bytes:
                        self.handle_error_code(return_bytes)
                    elif b'device' in return_bytes:
                        self.handle_error_code(return_bytes)
                    elif b'device_list' in return_bytes:
                        for i in range(4, len(return_bytes), 2):
                            if return_bytes[i + 1] == b'Empatica_E4':
                                self.device_list.append(return_bytes[i])
                    elif b'device_connect' in return_bytes:
                        self.device.connected = True
                        self.device.start_window_timer()
                    elif b'device_subscribe' in return_bytes:
                        self.device.subscribed_streams[return_bytes[2].decode("utf-8")] = \
                            not self.device.subscribed_streams.get(return_bytes[2].decode("utf-8"))
                elif return_bytes[0][0:2] == b'E4':
                    self.handle_data_stream(return_bytes)
            except ConnectionAbortedError:
                pass
            except ConnectionResetError:
                pass
            except ConnectionError:
                pass

    def stop_reading_thread(self):
        """
        Sets the reading thread variable to False to stop the reading thread.
        :return: None.
        """
        self.reading = False

    @staticmethod
    def handle_error_code(error):
        """
        Parses error code for formatting in Exception message.
        :param error: bytes-like error message.
        :return: None.
        """
        message = ""
        for err in error:
            message = message + err.decode("utf-8") + " "
        raise EmpaticaCommandError(message)

    def handle_data_stream(self, data):
        """
        Parses and saves the data received from the Empatica Server.
        :param data: bytes-like packet.
        :return: None.
        """
        try:
            self.readings += 1
            data_type = data[0][3:]
            if data_type == b'Acc':
                self.device.acc_3d.append(float(data[2]))
                self.device.acc_3d.append(float(data[3]))
                self.device.acc_3d.append(float(data[4]))
                self.device.acc_x.append(float(data[2]))
                self.device.acc_y.append(float(data[3]))
                self.device.acc_z.append(float(data[4]))
                self.device.acc_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Bvp':
                self.device.bvp.append(float(data[2]))
                self.device.bvp_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Gsr':
                self.device.gsr.append(float(data[2]))
                self.device.gsr_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Temperature':
                self.device.tmp.append(float(data[2]))
                self.device.tmp_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Ibi':
                self.device.ibi.append(float(data[2]))
                self.device.ibi_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Hr':
                self.device.hr.append(float(data[2]))
                self.device.hr_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Battery':
                self.device.bat.append(float(data[2]))
                self.device.bat_timestamps.append(float(data[1]))
                pass
            elif data_type == b'Tag':
                self.device.tag.append(float(data[2]))
                self.device.tag_timestamps(float(data[1]))
                pass
            else:
                raise EmpaticaDataError(data)
        except:
            raise EmpaticaDataError(data)

    def list_connected_devices(self):
        """
        Sends the list connected devices command to get the devices auto-connected over BLE.
        :return: None
        """
        self.socket_conn.send(b'device_list\r\n')

Static methods

def handle_error_code(error)

Parses error code for formatting in Exception message. :param error: bytes-like error message. :return: None.

Expand source code
@staticmethod
def handle_error_code(error):
    """
    Parses error code for formatting in Exception message.
    :param error: bytes-like error message.
    :return: None.
    """
    message = ""
    for err in error:
        message = message + err.decode("utf-8") + " "
    raise EmpaticaCommandError(message)

Methods

def close(self)

Stops the reading thread and closes the socket. :return: None.

Expand source code
def close(self):
    """
    Stops the reading thread and closes the socket.
    :return: None.
    """
    self.stop_reading_thread()
    self.socket_conn.close()
def handle_data_stream(self, data)

Parses and saves the data received from the Empatica Server. :param data: bytes-like packet. :return: None.

Expand source code
def handle_data_stream(self, data):
    """
    Parses and saves the data received from the Empatica Server.
    :param data: bytes-like packet.
    :return: None.
    """
    try:
        self.readings += 1
        data_type = data[0][3:]
        if data_type == b'Acc':
            self.device.acc_3d.append(float(data[2]))
            self.device.acc_3d.append(float(data[3]))
            self.device.acc_3d.append(float(data[4]))
            self.device.acc_x.append(float(data[2]))
            self.device.acc_y.append(float(data[3]))
            self.device.acc_z.append(float(data[4]))
            self.device.acc_timestamps.append(float(data[1]))
            pass
        elif data_type == b'Bvp':
            self.device.bvp.append(float(data[2]))
            self.device.bvp_timestamps.append(float(data[1]))
            pass
        elif data_type == b'Gsr':
            self.device.gsr.append(float(data[2]))
            self.device.gsr_timestamps.append(float(data[1]))
            pass
        elif data_type == b'Temperature':
            self.device.tmp.append(float(data[2]))
            self.device.tmp_timestamps.append(float(data[1]))
            pass
        elif data_type == b'Ibi':
            self.device.ibi.append(float(data[2]))
            self.device.ibi_timestamps.append(float(data[1]))
            pass
        elif data_type == b'Hr':
            self.device.hr.append(float(data[2]))
            self.device.hr_timestamps.append(float(data[1]))
            pass
        elif data_type == b'Battery':
            self.device.bat.append(float(data[2]))
            self.device.bat_timestamps.append(float(data[1]))
            pass
        elif data_type == b'Tag':
            self.device.tag.append(float(data[2]))
            self.device.tag_timestamps(float(data[1]))
            pass
        else:
            raise EmpaticaDataError(data)
    except:
        raise EmpaticaDataError(data)
def handle_reading_receive(self)

Parses and handles packets received from the Empatica Server. :return: None.

Expand source code
def handle_reading_receive(self):
    """
    Parses and handles packets received from the Empatica Server.
    :return: None.
    """
    while self.reading:
        try:
            return_bytes = self.socket_conn.recv(4096)
            return_bytes = return_bytes.split()
            if return_bytes[0] == b'R':
                if b'ERR' in return_bytes:
                    self.handle_error_code(return_bytes)
                elif b'connection' in return_bytes:
                    self.handle_error_code(return_bytes)
                elif b'device' in return_bytes:
                    self.handle_error_code(return_bytes)
                elif b'device_list' in return_bytes:
                    for i in range(4, len(return_bytes), 2):
                        if return_bytes[i + 1] == b'Empatica_E4':
                            self.device_list.append(return_bytes[i])
                elif b'device_connect' in return_bytes:
                    self.device.connected = True
                    self.device.start_window_timer()
                elif b'device_subscribe' in return_bytes:
                    self.device.subscribed_streams[return_bytes[2].decode("utf-8")] = \
                        not self.device.subscribed_streams.get(return_bytes[2].decode("utf-8"))
            elif return_bytes[0][0:2] == b'E4':
                self.handle_data_stream(return_bytes)
        except ConnectionAbortedError:
            pass
        except ConnectionResetError:
            pass
        except ConnectionError:
            pass
def list_connected_devices(self)

Sends the list connected devices command to get the devices auto-connected over BLE. :return: None

Expand source code
def list_connected_devices(self):
    """
    Sends the list connected devices command to get the devices auto-connected over BLE.
    :return: None
    """
    self.socket_conn.send(b'device_list\r\n')
def recv(self)

Blocking method to receive a packet from the Empatica Server. :return: bytes-like.

Expand source code
def recv(self):
    """
    Blocking method to receive a packet from the Empatica Server.
    :return: bytes-like.
    """
    return self.socket_conn.recv(4096)
def send(self, packet)

Blocking method to send a packet to the Empatica Server. :param packet: str command :return: None.

Expand source code
def send(self, packet):
    """
    Blocking method to send a packet to the Empatica Server.
    :param packet: str command
    :return: None.
    """
    self.socket_conn.send(packet)
def start_receive_thread(self)

Starts the receiving thread to handle responses from Empatica Server. :return: None.

Expand source code
def start_receive_thread(self):
    """
    Starts the receiving thread to handle responses from Empatica Server.
    :return: None.
    """
    self.reading = True
    self.reading_thread = threading.Thread(target=self.handle_reading_receive)
    self.reading_thread.start()
def stop_reading_thread(self)

Sets the reading thread variable to False to stop the reading thread. :return: None.

Expand source code
def stop_reading_thread(self):
    """
    Sets the reading thread variable to False to stop the reading thread.
    :return: None.
    """
    self.reading = False
class EmpaticaCommandError (*args, **kwargs)

Custom exception for when an Empatica response is an error message.

Expand source code
class EmpaticaCommandError(Exception):
    """
    Custom exception for when an Empatica response is an error message.
    """
    pass

Ancestors

  • builtins.Exception
  • builtins.BaseException
class EmpaticaDataError (*args, **kwargs)

Custom exception for when there is an error when parsing a data message.

Expand source code
class EmpaticaDataError(Exception):
    """
    Custom exception for when there is an error when parsing a data message.
    """
    pass

Ancestors

  • builtins.Exception
  • builtins.BaseException
class EmpaticaDataStreams

Applicable data streams that can be received from the Empatica server.

Expand source code
class EmpaticaDataStreams:
    """
    Applicable data streams that can be received from the Empatica server.
    """
    ACC = b'acc'
    BAT = b'bat'
    BVP = b'bvp'
    GSR = b'gsr'
    IBI = b'ibi'
    TAG = b'tag'
    TMP = b'tmp'
    ALL_STREAMS = [b'acc', b'bat', b'bvp', b'gsr', b'ibi', b'tag', b'tmp']

Class variables

var ACC
var ALL_STREAMS
var BAT
var BVP
var GSR
var IBI
var TAG
var TMP
class EmpaticaE4 (device_name, window_size=None)

Class to wrap the client socket connection and configure the data streams.

Initializes the socket connection and connects the Empatica E4 specified. :param device_name: str: The Empatica E4 to connect to

Expand source code
class EmpaticaE4:
    """
    Class to wrap the client socket connection and configure the data streams.
    """

    def __init__(self, device_name, window_size=None):
        """
        Initializes the socket connection and connects the Empatica E4 specified.
        :param device_name: str: The Empatica E4 to connect to
        """
        self.window_size = window_size
        self.window_thread = threading.Thread(target=self.timer_thread)
        self.client = EmpaticaClient()
        self.connected = False
        self.connect(device_name)
        while not self.connected:
            pass
        self.suspend_streaming()
        self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps = [], [], [], [], []
        self.bvp, self.bvp_timestamps = [], []
        self.gsr, self.gsr_timestamps = [], []
        self.tmp, self.tmp_timestamps = [], []
        self.tag, self.tag_timestamps = [], []
        self.ibi, self.ibi_timestamps = [], []
        self.bat, self.bat_timestamps = [], []
        self.hr, self.hr_timestamps = [], []
        self.windowed_readings = []
        self.subscribed_streams = {
            "acc": False,
            "bvp": False,
            "gsr": False,
            "tmp": False,
            "tag": False,
            "ibi": False,
            "bat": False
        }

    def start_window_timer(self):
        if self.window_size:
            self.window_thread.start()

    def timer_thread(self):
        if self.window_size:
            while self.connected:
                time.sleep(self.window_size)
                self.split_window()

    def split_window(self):
        # Save all the readings to our window
        self.windowed_readings.append(
            (self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps,
             self.bvp, self.bvp_timestamps,
             self.gsr, self.gsr_timestamps,
             self.tmp, self.tmp_timestamps,
             self.tag, self.tag_timestamps,
             self.ibi, self.ibi_timestamps,
             self.bat, self.bat_timestamps,
             self.hr, self.hr_timestamps)
        )
        # Clear all readings collected so far
        self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps = [], [], [], [], []
        self.bvp, self.bvp_timestamps = [], []
        self.gsr, self.gsr_timestamps = [], []
        self.tmp, self.tmp_timestamps = [], []
        self.tag, self.tag_timestamps = [], []
        self.ibi, self.ibi_timestamps = [], []
        self.bat, self.bat_timestamps = [], []
        self.hr, self.hr_timestamps = [], []

    def close(self):
        """
        Closes the socket connection.
        :return: None.
        """
        self.connected = False
        self.client.close()

    def send(self, command):
        """
        Blocking method to send data to Empatica Server.
        :param command: bytes-like: data to send
        :return: None.
        """
        self.client.send(command)

    def receive(self):
        """
        Blocking method to receive data from Empatica Server.
        :return: bytes-like: packet received.
        """
        return self.client.recv()

    def connect(self, device_name):
        """
        Sends the connect command packet to the Empatica Server.
        :param device_name: bytes-like: Empatica E4 to connect to
        :return: None.
        """
        command = b'device_connect ' + device_name + b'\r\n'
        self.send(command)
        self.client.device = self

    def disconnect(self):
        """
        Sends the disconnect command packet to the Empatica Server.
        :return: None.
        """
        command = b'device_disconnect\r\n'
        self.send(command)
        self.client.stop_reading_thread()

    def save_readings(self, filename):
        """
        Saves the readings currently collected to the specified filepath.
        :param filename: str: full path to file to save to
        :return: None.
        """
        if self.windowed_readings:
            with open(filename, "wb") as file:
                pickle.dump(self.windowed_readings, file)
        else:
            with open(filename, "w") as file:
                for reading in self.acc_3d:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.acc_x:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.acc_y:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.acc_z:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.acc_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.gsr:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.gsr_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.bvp:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.bvp_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.tmp:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.tmp_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.hr:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.hr_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.ibi:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.ibi_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.bat:
                    file.write(str(reading) + ",")
                file.write("\n")
                for reading in self.bat_timestamps:
                    file.write(str(reading) + ",")
                file.write("\n")

    def clear_readings(self):
        """
        Clears the readings collected.
        :return: None.
        """
        self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps = [], [], [], [], []
        self.bvp, self.bvp_timestamps = [], []
        self.gsr, self.gsr_timestamps = [], []
        self.tmp, self.tmp_timestamps = [], []
        self.tag, self.tag_timestamps = [], []
        self.ibi, self.ibi_timestamps = [], []
        self.bat, self.bat_timestamps = [], []
        self.hr, self.hr_timestamps = [], []

    def subscribe_to_stream(self, stream):
        """
        Subscribes the socket connection to a data stream, blocks until the Empatica Server responds.
        :param stream: bytes-like: data to stream.
        :return: None.
        """
        command = b'device_subscribe ' + stream + b' ON\r\n'
        self.send(command)
        while not self.subscribed_streams.get(stream.decode("utf-8")):
            pass

    def unsubscribe_from_stream(self, stream):
        """
        Unsubscribes the socket connection from a data stream, blocks until the Empatica Server responds.
        :param stream: bytes-like: data to stop streaming.
        :return: None.
        """
        command = b'device_subscribe ' + stream + b' OFF\r\n'
        self.send(command)
        while self.subscribed_streams.get(stream.decode("utf-8")):
            pass

    def suspend_streaming(self):
        """
        Stops the data streaming from the Empatica Server for the Empatica E4.
        :return: None.
        """
        command = b'pause ON\r\n'
        self.send(command)

    def start_streaming(self):
        """
        Starts the data streaming from the Empatica Server for the Empatica E4.
        :return: None.
        """
        command = b'pause OFF\r\n'
        self.send(command)

Methods

def clear_readings(self)

Clears the readings collected. :return: None.

Expand source code
def clear_readings(self):
    """
    Clears the readings collected.
    :return: None.
    """
    self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps = [], [], [], [], []
    self.bvp, self.bvp_timestamps = [], []
    self.gsr, self.gsr_timestamps = [], []
    self.tmp, self.tmp_timestamps = [], []
    self.tag, self.tag_timestamps = [], []
    self.ibi, self.ibi_timestamps = [], []
    self.bat, self.bat_timestamps = [], []
    self.hr, self.hr_timestamps = [], []
def close(self)

Closes the socket connection. :return: None.

Expand source code
def close(self):
    """
    Closes the socket connection.
    :return: None.
    """
    self.connected = False
    self.client.close()
def connect(self, device_name)

Sends the connect command packet to the Empatica Server. :param device_name: bytes-like: Empatica E4 to connect to :return: None.

Expand source code
def connect(self, device_name):
    """
    Sends the connect command packet to the Empatica Server.
    :param device_name: bytes-like: Empatica E4 to connect to
    :return: None.
    """
    command = b'device_connect ' + device_name + b'\r\n'
    self.send(command)
    self.client.device = self
def disconnect(self)

Sends the disconnect command packet to the Empatica Server. :return: None.

Expand source code
def disconnect(self):
    """
    Sends the disconnect command packet to the Empatica Server.
    :return: None.
    """
    command = b'device_disconnect\r\n'
    self.send(command)
    self.client.stop_reading_thread()
def receive(self)

Blocking method to receive data from Empatica Server. :return: bytes-like: packet received.

Expand source code
def receive(self):
    """
    Blocking method to receive data from Empatica Server.
    :return: bytes-like: packet received.
    """
    return self.client.recv()
def save_readings(self, filename)

Saves the readings currently collected to the specified filepath. :param filename: str: full path to file to save to :return: None.

Expand source code
def save_readings(self, filename):
    """
    Saves the readings currently collected to the specified filepath.
    :param filename: str: full path to file to save to
    :return: None.
    """
    if self.windowed_readings:
        with open(filename, "wb") as file:
            pickle.dump(self.windowed_readings, file)
    else:
        with open(filename, "w") as file:
            for reading in self.acc_3d:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.acc_x:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.acc_y:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.acc_z:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.acc_timestamps:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.gsr:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.gsr_timestamps:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.bvp:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.bvp_timestamps:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.tmp:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.tmp_timestamps:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.hr:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.hr_timestamps:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.ibi:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.ibi_timestamps:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.bat:
                file.write(str(reading) + ",")
            file.write("\n")
            for reading in self.bat_timestamps:
                file.write(str(reading) + ",")
            file.write("\n")
def send(self, command)

Blocking method to send data to Empatica Server. :param command: bytes-like: data to send :return: None.

Expand source code
def send(self, command):
    """
    Blocking method to send data to Empatica Server.
    :param command: bytes-like: data to send
    :return: None.
    """
    self.client.send(command)
def split_window(self)
Expand source code
def split_window(self):
    # Save all the readings to our window
    self.windowed_readings.append(
        (self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps,
         self.bvp, self.bvp_timestamps,
         self.gsr, self.gsr_timestamps,
         self.tmp, self.tmp_timestamps,
         self.tag, self.tag_timestamps,
         self.ibi, self.ibi_timestamps,
         self.bat, self.bat_timestamps,
         self.hr, self.hr_timestamps)
    )
    # Clear all readings collected so far
    self.acc_3d, self.acc_x, self.acc_y, self.acc_z, self.acc_timestamps = [], [], [], [], []
    self.bvp, self.bvp_timestamps = [], []
    self.gsr, self.gsr_timestamps = [], []
    self.tmp, self.tmp_timestamps = [], []
    self.tag, self.tag_timestamps = [], []
    self.ibi, self.ibi_timestamps = [], []
    self.bat, self.bat_timestamps = [], []
    self.hr, self.hr_timestamps = [], []
def start_streaming(self)

Starts the data streaming from the Empatica Server for the Empatica E4. :return: None.

Expand source code
def start_streaming(self):
    """
    Starts the data streaming from the Empatica Server for the Empatica E4.
    :return: None.
    """
    command = b'pause OFF\r\n'
    self.send(command)
def start_window_timer(self)
Expand source code
def start_window_timer(self):
    if self.window_size:
        self.window_thread.start()
def subscribe_to_stream(self, stream)

Subscribes the socket connection to a data stream, blocks until the Empatica Server responds. :param stream: bytes-like: data to stream. :return: None.

Expand source code
def subscribe_to_stream(self, stream):
    """
    Subscribes the socket connection to a data stream, blocks until the Empatica Server responds.
    :param stream: bytes-like: data to stream.
    :return: None.
    """
    command = b'device_subscribe ' + stream + b' ON\r\n'
    self.send(command)
    while not self.subscribed_streams.get(stream.decode("utf-8")):
        pass
def suspend_streaming(self)

Stops the data streaming from the Empatica Server for the Empatica E4. :return: None.

Expand source code
def suspend_streaming(self):
    """
    Stops the data streaming from the Empatica Server for the Empatica E4.
    :return: None.
    """
    command = b'pause ON\r\n'
    self.send(command)
def timer_thread(self)
Expand source code
def timer_thread(self):
    if self.window_size:
        while self.connected:
            time.sleep(self.window_size)
            self.split_window()
def unsubscribe_from_stream(self, stream)

Unsubscribes the socket connection from a data stream, blocks until the Empatica Server responds. :param stream: bytes-like: data to stop streaming. :return: None.

Expand source code
def unsubscribe_from_stream(self, stream):
    """
    Unsubscribes the socket connection from a data stream, blocks until the Empatica Server responds.
    :param stream: bytes-like: data to stop streaming.
    :return: None.
    """
    command = b'device_subscribe ' + stream + b' OFF\r\n'
    self.send(command)
    while self.subscribed_streams.get(stream.decode("utf-8")):
        pass
class EmpaticaServerConnectError (*args, **kwargs)

Custom exception for when the socket fails to connect to the Empatica Server.

Expand source code
class EmpaticaServerConnectError(Exception):
    """
    Custom exception for when the socket fails to connect to the Empatica Server.
    """
    pass

Ancestors

  • builtins.Exception
  • builtins.BaseException