def post(self, request, device_id):
        """
        Send data to device serial
        """

        try:
            data = request.DATA['data']
        except KeyError:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        try:
            is_encoded = request.DATA['is_base64']
            needs_encoding = not strtobool(is_encoded)
        except KeyError:
            needs_encoding = True
        except ValueError:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        if needs_encoding:
            data = base64.b64encode(data)

        username, password, cloud_fqdn = get_credentials(request)

        if not username or not password or not cloud_fqdn:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        conn = DeviceCloudConnector(username, password, cloud_fqdn)

        try:
            response = conn.send_serial_data(device_id, data)
        except HTTPError, e:
            return Response(status=e.response.status_code,
                            data=e.response.text)
    def put(self, request, device_id):

        io_command_pairs = []
        io_set_setting_pairs = []
        io_serial_data_values = []

        # Sanitize and sort inputs
        for name, value in request.DATA.iteritems():
            # May get '/' seperators, mixed case, etc. Strip out non-alphanum
            # chars, make uppercase.
            name = str(name).translate(None, self.delchars).upper()

            # Check for DIO and extract the bit position of this pin
            match = re.match(r"(DIO)(?P<bit>[0-9]+)", name)
            if match:
                bit = match.group('bit')

                # Convert boolean-ish values to True/False
                if type(value) == str or type(value) == unicode:
                    try:
                        value = bool(strtobool(value))
                    except ValueError:
                        # Try to catch "high"/"low"
                        if value.lower() == "high":
                            value = True
                        elif value.lower() == "low":
                            value = False
                        else:
                            return Response(status=status.HTTP_400_BAD_REQUEST)
                io_command_pairs.append((int(bit), value))
            # Else see if it looks like a traditional AT command
            elif len(name) == 2:  # M0, etc AT command, used for PWM setting
                # Some commands require hex strings, others integers, others
                # arbitrary text...ug
                try:
                    # Hex
                    if name in ['M0', 'M1', 'IC', 'PR', 'PD', 'DS']:
                        val_str = hex(int(value))
                    elif (name in ['LT', 'RP', 'IR', 'IF'] or
                            name.startswith('T') or name.startswith('Q')):
                        val_str = str(int(value))
                    else:
                        # Use as is
                        val_str = str(value)
                except ValueError:
                    return Response(status=status.HTTP_400_BAD_REQUEST)

                io_set_setting_pairs.append((name, val_str))
            # Handle serial output. Currently don't support sending to
            # different targets, so combine all
            # serial messages into a single payload
            elif name.startswith("SERIAL"):
                io_serial_data_values.append(value)
            else:
                # Unknown command provided
                return Response(status=status.HTTP_400_BAD_REQUEST)

        username, password, cloud_fqdn = get_credentials(request)

        if not username or not password or not cloud_fqdn:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        conn = DeviceCloudConnector(username, password, cloud_fqdn)

        resp = {}

        try:
            # For IO command, need to generate two bitmasks - enable and level
            if len(io_command_pairs):
                enable_mask = 0
                output_mask = 0
                for bit, value in io_command_pairs:
                    enable_mask |= 1 << int(bit)
                    output_mask |= value << int(bit)
                resp = conn.set_output(device_id,
                                       hex(enable_mask), hex(output_mask))

            if len(io_set_setting_pairs):
                # Because these settings belong to a single known group, we can
                # construct the request for the user
                new_settings = {'InputOutput': {}}
                for name, val in io_set_setting_pairs:
                    new_settings['InputOutput'][name] = val
                resp = conn.set_device_settings(device_id, new_settings)

            if len(io_serial_data_values):
                data = "".join(io_serial_data_values)
                data = base64.b64encode(data)
                resp = conn.send_serial_data(device_id, data)

        except HTTPError, e:
            return Response(status=e.response.status_code,
                            data=e.response.text)