Now monitoring my websites with Google Spreadsheets and Pushover

I used a previous version of this free monitoring tool, but I recently setup their new version. It is quite neat. Basically, it uses Google’s infrastructure to constantly hit your website and if it doesn’t get a 200 status back, it will contact you via email or SMS saying your site is down. While this was nice, I really wanted to use Pushover more. So I looked through their API and discovered it is very easy to send messages. So I modified their script and now I get notifications via Pushover. Here are the changes that you need to perform.

  1. Add two new rows after the SMS notifications row.
  2. Call the title’s what you want, but I went with “Pushover API Token ::” on A5 and “Pushover User Key ::” on A6.
  3. I created a new Pushover app
  4. I put the new app’s API token in B5 and my user key in B6.5. Go to Tools | Script editor…
  5. Then add the following code to the function logMessage(url, message) funtion in the script after the email portion:
    if (sheet.getRange(“B5”).getValue() != “”){           var options = {               ‘method’: ‘post’            };    var url = ‘’     + ‘token=’ + sheet.getRange(“B5”).getValue()    + ‘&user=’ + sheet.getRange(“B6”).getValue()    + ‘&message=’ + alert    + ‘&title=’ + “Site ” + message;    var response = UrlFetchApp.fetch(url, options);        }

Controlling a Epson TM-T20 on the IBM i (or iSeries)

The past couple days I was on a mission to look for a way to replace our aging dot-matrix receipt printer. We got some new printers to go with our new credit card processor. These printers are Epson TM-T20 thermal printers. I had some problems finding the information to have the printer cut the paper and to get it to eject the cash drawer. I am documenting what I found here for people in the future to figure out.

You need these codes embedded in the spool file. The first hex character, 03, tells the OS we are sending ASCII characters. The second, 06, tells the OS that the next x characters are ASCII.

  • To cut the paper – X'03060D0A1D564220'
  • To eject the cash drawer 1 – X'03051B703040F0'
  • To eject the cash drawer 2 – X'03051B703140F0'

Add a custom IBM i library list to a C# application

Ever find that you need to add a custom library list to a C# application that works with IBM i data? Here is what you need to do. Add LibraryList= to your connection string. So that would look like:


Creating a better emailed statement in RPG using CGIDEV2

We had some… problems with our utility bill emails. They weren’t worded clearly enough that some people were getting confused. See, when I wrote this, used the only approach I knew and manually assembled a string of text until the email was created (depended upon if they were auto-pay or not), but over all the emails were very similar. We like to be customer friendly, so we decided we better make the emails clearer.

After working with our communications department, they came up with a very good set of emails… that don’t work well with how I am currently assembling the emails. Damn!

So, I walked back to my desk feeling like “Oh crap! I have a lot of changes to do.” Then I had the thought that too bad I can’t template… oh yes I can! CGIDEV2 does that! Since I haven’t done much in that field, I figured I would hit up the WEB400 community to see what else there is out there. I came back with a list of a few frameworks, but most people basically said that CGIDEV2 was the best place to start. One helpful person, gave me the following list:

I decided to use CGIDEV2 as that was the most promoted package. After I had it installed, tried to figure out what I needed in order to do what I want. Here is the minimal information that is needed:

First you need to add the following two copybooks to your D-specs:

/copy CGIDEV2/qrpglesrc,prototypeb
/copy CGIDEV2/qrpglesrc,usec

Then in my loop I did the following:

dow (not %eof());

// Clear the buffer to make sure it is empty.
callp ClrHtmlBuffer();

// Pull the correct message.
if (autopay());
callp gethtmlIFS(‘/utilityBillText/UtilityBillAutoPay.txt’);
callp gethtmlIFS(‘/utilityBillText/UtilityBillRegular.txt’);

// Update the variables in the message
callp updHTMLvar(‘custnum’:%editw(CustN:’0 – ‘));
callp updHTMLvar(‘serviceaddr’:%trim(Adrs));
callp updHTMLvar(‘balance’:%char(balance));
callp updHTMLvar(‘duedate’:%char(%date(DueDt):*usa));

// “Write” the file into the buffer
callp wrtsection(’email_body’);
// Save the file to the IFS in the tmp directory.
rc = WrtHtmlToStmf(‘/tmp/emailbody.txt’);

// Send email through MailTool (

read FILE;


Suprisingly this worked the first time! Now if the communications department ever wants to change the email, all I need to do is update the text and the email is now changed.

I hope this helps others that are looking at doing this.

How to Debug a Batch Program

Via BVS/Tools – FAQ

How do I debug an RPG program in batch?
Submit your program into batch and make sure it is held. Do this by either holding the job queue that you are submitting the job to, or use the HOLD(*YES) option on the SBMJOB command.

  • Use the WRKUSRJOB and display the job you with to debug with the display job option (5).
  • Write down the user name, job name and number.
  • Start a service job using STRSRVJOB entering the name, job name and number from the previous step.
  • STRDBG PGM(YOURPGM) – Press F12 to exit the source display (Sorry, can’t enter breakpoints yet).
  • Release your submitted job by releasing the job queue or the job itself.
  • A display will appear asking you to press F10 function key. Press F10 and you will be brought to a command line.
  • Enter DSPMODSRC and enter your breakpoints.
  • Leave source display and command line by pressing F3 until you are back to the screen that asks you to press F10 to enter breakpoints.
  • Press Enter to start your job.
  • After that the job begins running and stops at the first breakpoint reached.
    Also, you can create a simple CL to combine the two commands to make it easier to use. I created a command STRBTCHDBG so it is really simple to use. I might post the source to that later.

Chili Bread Bowls

I have been loving the Artisan Bread in 5 Minutes series of books so today when we thought that making bread bowls and chili sounded good I went to my bible of bread.

To start with, make your own favorite chili and let it simmer all day in the crock pot (because it tastes better that way).

I then turned to the original Artisan Bread in 5 Minutes a day book. I choose the master recipe, the boule since it was easy to work with and I knew it had a nice crust to prevent it from loosing shape when the chili was added. I tried several sizes of loaves to see what size worked the best. We discovered that while the 1/2 lb loaf was too small to hold more than 1 scoop of chili, it was just the right size for a complete meal.

Loaves of bread cooling

The 1/4 lb loaves were way too small. The 3/8 (or so) lb loaves were just right for the kids.

All I did was take a sharp knife and cut a circle in the top pushing the blade most of the way in. Then pull out the bread until it was a good bowl. I did nothing special on the inside beyond that. Now you can see the finished product.

The only problem? It takes so long to bake enough bread if we were to have more guests!

How do I iterate over a set of records in RPG with embedded SQL?

A person on StackOverflow asked a good question a while back.

How do I iterate over a set of records in RPG(LE) with embedded SQL?

This is a mix of information gleamed from many sources. I use a standard template for every program and modify as needed from there.

Here is the core of the program I use for SQLRPGLE programs:

       // SQL Fields
     D sqlStatement    S           2048A   varying
     D SqlResult       DS                  qualified
      /copy modules/qcopysrc,sql_h

       // Internal Subprocedures
     D Init            PR
     D Main            PR
     D BuildSqlStatement...
     D                 PR
     D OpenCursor      PR             5A
     D FetchNext       PR             5A
     D CloseCursor     PR             5A
       // Entry Parms
     D T_RPT_SQL       PR                  extpgm('T_RPT_SQL')
     D T_RPT_SQL       PI
       *inlr = *on;

     P* Procedure name: Init
     P* Purpose:
     P* Returns:
     P Init            B
     D Init            PI
       exec sql
         set option naming = *sys, commit = *none, usrprf = *user,
                    dynusrprf = *user, datfmt = *iso, closqlcsr = *endmod;
     P Init            E

     P* Procedure name: Main
     P* Purpose:
     P* Returns:
     P Main            B
     D Main            PI
       if (OpenCursor() = SQL_SUCCESS);
           dow (FetchNext() = SQL_SUCCESS);
               // *** INSERT CODE HERE ***
     P Main            E

     P* Procedure name: BuildSqlStatement
     P* Purpose:
     P* Returns:
     P BuildSqlStatement...
     P                 B
     D BuildSqlStatement...
     D                 PI
       gSqlStatement = ' ';
     P BuildSqlStatement...
     P                 E

     P* Procedure name: OpenCursor
     P* Purpose:
     P* Returns:
     P OpenCursor      B
     D OpenCursor      PI             5A
       exec sql
         close c1;
       exec sql
         prepare sel from :sqlStatement;
       if sqlStt = SQL_SUCCESS;
         exec sql
           declare c1 cursor for sel;
         exec sql
           open c1;
       return sqlStt;
     P OpenCursor      E

     P* Procedure name: FetchNext
     P* Purpose:
     P* Returns:
     P FetchNext       B
     D FetchNext       PI             5A
       exec sql
         fetch c1 into :SqlResult;
       return sqlstt;
     P FetchNext       E

     P* Procedure name: CloseCusor
     P* Purpose:
     P* Returns:
     P CloseCursor     B
     D CloseCursor     PI             5A
       exec sql
         close c1;
       return sqlstt;
     P CloseCursor     E

Let’s break this down some. First, the copybook sql_h defines some standard SQL return statuses including SQL_SUCCESS (which is 00000). My mainline has three lines of code:


  *inlr = *on;

This is for when using RDi and WDSc outlines. I can quickly jump to any portion of the code. You can’t jump to the beginning of the mainline code that I know of in the outline.

Next, we have the BuildSqlStatement() subprocedure. I used this mostly for when I need a dynamically built SQL statement. I hope OpenCursor(), CloseCusor(), and FetchNext() are self-explanatory. One thing I want to note, I realized that SQLSTT is a global value but if you return it in the subprocedure, you can then do shorten the calling code to if (OpenCursor() = SQL_SUCCESS);.

So this is how I iterate over a set of records using SQL in RPG. Is there a better way? I am open to suggestions!

If you like this article, please up-vote my answer.

First Data Global Gateway Web Service API setup for Visual Studio 2010

First Data’s directions for setting up their web service in Visual Studio is very confusing and I ended up calling them for help. Visual Studio 2010 has different directions than previous versions of Visual Studio. Here is how I got it to work.

First install the certificate, go into Internet Options in the Control Panel. Choose the Content tab then Certificates. On the Personal tab, click Import… and follow the wizard here to add your certificate. I did this mostly by accident only because I have had to do this before for other stuff I have done in the past. You might not need to do those other instructions in their documentation. I am using Windows XP, so your mileage may vary. (Bonus points if you leave a comment with Vista/7 instructions.)

After that you can setup the web service:

  1. Download all of the files they list in the documentation. I put them all into C:\FDGGWSClient. Put a1.xsd, fdggwsapi.xsd, and v1.xsd into C:\FDGGWSClient\schemas_us. Put order.wsdl into C:\FDGGWSClient\wsdl. I also put the certificate (WSxxxxxxxx._.1.pem) in the root folder (C:\FDGGWSClient).
  2. In Visual Studio 2010, right click on References and choose Add Service Reference. Click on Advanced… then on the bottom of the new window click Add Web Reference…
  3. The URL you enter here is the file path to order.wsdl. In my case it’s C:\FDGGWSClient\wsdl\order.wsdl.

This should allow it to work.

The other thing I did was create a separate class for all of the processing. So my constructor had:

    private FDGGWSApiOrderService oFDGGWSApiOrderService = null;

    /// Initializes a new instance of the test version of the  class.
    /// if set to true [test].
    public ProcessCreditCard()
        ServicePointManager.Expect100Continue = false;
        // Initialize Service Object 
        oFDGGWSApiOrderService = new FDGGWSApiOrderService();
        // Set the WSDL URL
        oFDGGWSApiOrderService.Url = @Settings.Default.CcApiUrl;
        // Configure Client Certificate  
        // Set the Authentication Credentials
        NetworkCredential nc = new NetworkCredential(Settings.Default.CertUser, Settings.Default.CertPass);
        oFDGGWSApiOrderService.Credentials = nc;

Then I created a method to create the rest of the information needed to send the transaction to them.

First Data has been notorious on how to set up and start using their services. I hope this helps you get started.

Like this? Give my answer some love on StackOverflow.

My tips for people looking for jobs in the programming field

So you are graduating (or have graduated) from college and are now looking for a job? My I know my cousin is in the same boat. I have been giving him some tips to get that extra edge. Here are some of those tips that I have been telling him.

  1. Go on StackOverflow and answer as many questions as possible. Keep to areas you either know or want to know more about. If you can, write solid, tested examples. Otherwise do the research (aka Google, DuckDuckGo) to find an answer for them. Idea is win-win for you. First of all, it increases your reputation on a site where some developers have been hired because of their reputation score and answers. Secondly, an employer can see how you can communicate in written form and how you solve problems. I would highly recommend this be your part-time job (you know after the full-time job of looking for a job).
  2. Let’s face it, everyone can benefit from knowing some web programming these days. That is HTML5, JavaScript, and some minor CSS. Know enough to take on a project with confidence. You don’t have to write the next Facebook or Gmail, just a basic understanding of common UI tricks.
  3. The underlaying code of #2 is important as well. Play with one or two of the most popular frameworks of the area you want to be in.
  4. Joining #2 and #3 together; practice, practice, practice. Keep doing some sort of programming work. Maybe pick up an open-source project you are interested in working with. Grab some small bugs to begin with to gain some rep with the project maintainers.
  5. Network with others in the programming space and build relationships with them especially in the areas you want to focus on. That tends to be the best ways to find jobs. This should be done even before you finish school. Joining a local user group in your related field would be a good place to start.
  6. Read! Read about new methodologies, best-practices, and other technologies. I find Hacker News a great source for stories to read. Also take a look at my other website, Business Developer Talk, for links to articles I feel are relavant for the business developer.

I’ll call this article a work-in-progress. If anyone else any tips please include them in the comments.

Thanks to a few of my Tweeps, @mygeekdaddy, @gmantechi, @eglue, and @the_jamezp for some additional help in building this list.

Calendar Fix for iOS/iCal

This isn’t really a “fix” as it was something that I learned that was causing me a headache. My wife just got a Samsung DROID Charge and was sending me “meeting requests”. These appointments would be automatically added to my calendar without me accepting them. I finally realized that I had both and iCal running on my iMac. I looked through the settings in iCal and found a setting called “Automatically retrieve CalDAV invitations from Mail”. After unchecking this, my problem is fixed. I guess, that setting needs to be unchecked so I can see invites before they actually happen.