Setting up a Leviton VRCZ4-M0Z for use with Home Assistant

I’ve been so pleased with Home Assistant that I decided to see if I could migrate completely away from Vera and run all my Z-Wave devices on Home Assistant. Currently Home Assistant uses OpenZwave as the base and has basic support for a lot of Z-Wave devices. While OpenZwave isn’t as mature as Vera in its implementation, I found that with the exception of 4 Leviton VRCZ4-M0Z zone controllers, everything worked well. I’ve read that some people have had problems with Z-Wave on Home Assistant, but so far things have been going quite well for me using an Aeotec Z-Stick Gen5 as the controller. I suspect that my success is due to the type of devices I have (only outlets, switches, 1 light bulb, 2 portable MiniMote controllers and these VRCZ4s); I don’t have any sensors and only 2 of my devices are battery powered.

In order to complete my transition away from Vera, I had to get the 4 VRCZ4s to work. A Google search turned up very little information on how to do this with the exception of one post that gave me some clues. The post uses a third party piece of software that I don’t have to program the controllers, so that was pretty much out. However, the post did talk about the SCENE_CONTROLLER_CONF Z-Wave command class. With this information in hand, I decided to see if I could program my controllers.

Here’s what I eventually did.

Controller Programming

  1. Sign up for a developer account on Silicon Labs.
  2. Download the Z-Wave Developer kit, specifically the Z-Wave PC Controller software. (Windows only, but works fine in VMWare Fusion)
  3. Reset the VRCZ4 by pressing and holding the left side of buttons 1 & 3 until it blinks amber and then remains a solid red.
  4. Press left side of buttons 1 & 3 of the VRCZ4.
  5. Add node in Z-Wave config in HA
    Add Node
  6. Wait until node is added. I like to look the the OZW_Log.txt to check status. The node will say Complete next to it when it is done.
  7. Select node in Z-Wave config called Leviton VRCZ4-M0Z. Make a note of the node number.
    VRCZ4 Node
  8. Rename Entity by going to Node Information, selecting the gear and entering a new node.
  9. Under Node Group Associations, select Group 1
  10. Make sure Aeotec Z-Stick (node 1) is selected and click Add to Group.
    Screen Shot 2019 01 03 at 9 33 22 AM
  11. Repeat the above 2 steps for Groups 2-4. The VRCZ4 needs to know where to send commands when the buttons are pressed.
  12. If you have other devices you want to control such as lights or outlets and they support Z-Wave association, associate the devices with the VRCZ4 now. Groups 1-4 corresponds with buttons 1-4. So if you want button 1 to control an outlet, associate it with the outlet.
  13. If all the buttons on the VRCZ4 are going to be associated with other devices, you can stop here as the associations will cause the buttons to turn on/off the associated devices. You can even associate more than 1 device to a button (i.e. 2 backyard lights).
  14. If you want of the buttons to control scripts or automations on Home Assistant, you’ll have to do some more steps.
  15. Shutdown the HA box (not just HA, but the entire box).
  16. Connect Aeotec Z-Stick to computer with Z-Wave PC Controller software.
  17. Launch PC Controller software.
    Z-Wave PC Controller
  18. Click Command Classes
  19. Locate node of controller on left side.
  20. Click on it and then click Node Info.
    Command Classes
  21. Double click SCENE_CONTROLLER_CONF in the lower left.
  22. Change command to SCENE_CONTROLLER_CONF_SET.
    Change Command Class
  23. Click the button in the lower right to show the log.
  24. At this point, you need to program each button where you are not using associations to control devices.
  25. The Group IDs for the buttons are as follows:
    Button 1 on - 1
    Button 2 on - 2
    Button 3 on - 3
    Button 4 on - 4
    Button 1 off - 5
    Button 2 off - 6
    Button 3 off - 7
    Button 4 off - 8
    
  26. For each group ID, you have to assign a scene ID. Apparently the scene IDs should be unique across the entire Z-Wave network, but for my case I assigned the same scene IDs for each controller and can distinguish between the controllers in Home Assistant. You can choose whatever numbering scheme you want, but I went with the following:
    Button 1 on - 1
    Button 1 off - 2
    Button 2 on - 3
    Button 2 off - 4
    Button 3 on - 5
    Button 3 off - 6
    Button 4 on - 7
    Button 4 off - 8
    
  27. For each group ID, assign a scene ID and click send.
  28. You’ll see some message in the logs.
  29. To verify that the scene IDs are set, press a button and in the log you will see a SCENE_ACTIVATION_SET message with the scene ID.
    Z-Wave Log
  30. Note that the group IDs and the scene IDs are in hex. Since I’m only using 1-8, it doesn’t matter but if you use different scene IDs, be aware of this.
  31. Repeat for each button you want to set.
  32. Unplug the Aeotec Stick from the computer and plug it back into HA and reboot HA.

Using the Scenes

The programming has now been done, so the next step is to use the new commands in Home Assistant. I use Node-RED and have setup a sequence that handles all of my controllers.

Node-RED Sequence
1. Drag an events node (top left in Home Assistant) to the workspace and set it up to only look at zwave.scene_activated events.
Scene Events
2. Use a switch node to differentiate between the controllers.
Switch Node
3. Use a change node to extract the scene ID.
Extract Scene ID
4. Next use a switch node to separate out the scenes. If you didn’t follow my scene numbering, you will have to enter whatever scene IDs you used.
Separate out scenes
5. Connect up nodes for each scene.

While this seems like a lot of work, I probably took longer for me to write this up then to actually configure a controller! I’m not an expert in Home Assistant, Node-RED or Z-Wave so send feedback if you have any.

Enjoy!

Auto Layout in a UITableViewCell with an image

Auto Layout is an amazing concept for developing iOS apps as it allows for an application to more easily look good across different devices. In addition it makes using dynamic type so much easier; as someone that wears glasses I always increase the type size on my devices and when apps don’t take advantage of it, I get kind of annoyed. So when I develop apps, I try to use dynamic type and using auto layout makes things so much easier.

A common theme in apps I develop is to have a UITableView with a bunch of rows. The rows have different bits of text and the only sensible way to develop this is using auto layout. With the latest releases of iOS, a lot of code dealing with dynamic type, heights of cells, responding to device changes, etc. has been eliminated. However, there are still a few gotchas in making an app with a UITableView that behaves properly.

I’m going to go over the steps I used (and provide sample code) for how I handle this. I had a few requirements that make my implementation a little different than other tutorials on the web.

  • Each cell has an image that would be at most 1/3 the width of the screen.
  • The image must touch the top and bottom of the row.
  • The image had to be at least a certain height.
  • The image should attempt to have the aspect ratio of the original image. (Aspect Fit could leave white space; Aspect Fill could hide some of the image.)
  • Images are loaded asynchronously from the Internet.
  • Next to the image is up to 5 lines of text pinned to the top.
  • Next to the image is 1 line of text pinned to the bottom.
  • Each line of text could wrap to multiple lines.
  • Increasing the type sized must resize the rows.
  • The cell must have a minimum height.
  • Rotating the device must work.

Writing that out sure looks more complicated than it was in my head!

I’m not going to go over the initial project setup, but will jump right into Interface Builder after I created a UITableViewCell with xib. Note that I don’t use Storyboards and opt to create a separate xib for each view controller and each cell; Storyboards tend to bite me each time I use them as I like to re-use code as much as possible and by default the Storyboard for a UITableViewController puts the cell in the Storyboard making it harder to manage. I’m sure some would argue with me that Storyboards are great, but they just don’t work for me.

  1. Create a new UITableViewCell with a xib.
    Creating a UITableViewCell subclass
  2. In the xib, add a UIImageView to the left side that is pinned to the left, top, and bottom of the cell.
  3. Set the UIImageView to Aspect Fill and Clip to bounds.
  4. Give the UIImageView fixed width and height constraints. (We’ll change this later.)
  5. Add a vertical stack view pinned to 10 pixels from the UIImageView, pinned to 10 pixels from the top and 10 pixels from the right.
  6. Add 5 UILabels to the stack view. Each label should be set for “Automatically adjusts font” and a Font of Body. Set 1 or more of the labels to have 0 Lines so that it grows vertically. Also, set auto shrink to minimum font scale of 0.5.
  7. Add another vertical stack view pinned to the leading of the first stack view, 10 pixels from the right and bottom of the container and on the top to be >= 5 from the other stack view.
  8. Add a UILabel to this stack view. Set the font as above.
  9. Set the height constraint of the UIImageView to a priority of 250 (low).
  10. Add a UIActivityIndicator that is centered on the UIImageView (set the constraints).
  11. Create a UIImageView subclass that looks like this. The UIImageView uses the intrinsic size of the image in the absence of other constraints and we want more control of the height of the view.
    class ExampleImageView: UIImageView {
        var minimumHeight: CGFloat = 0
        override var intrinsicContentSize: CGSize {
            if minimumHeight == 0 {
                return super.intrinsicContentSize
            }
            return CGSize(width: 0, height: minimumHeight)
        }
    }
    
  12. Change the UIImageView class to ExampleImageView.

  13. Connect outlets for the 6 UILabels, the UIImageView (with the new class), the activity indicator and the height and width constraints on the UIImageView.

Your xib should look like this:

TableViewCell xib

Time to move into the source of the table view cell. I’m only going to cover the interesting parts here. See my example repo at for the full example.

  1. Setup a variable for the cell width. This is going to be set through the view controller so that rotation changes can change the width of the image.
    var cellWidth: CGFloat = 0 {
        didSet {
            maxImageWidth = cellWidth / 3
            setImageWidth()
        }
    }
    
    fileprivate var maxImageWidth: CGFloat = 120
    
  2. Add a method for setting the image width.
    fileprivate func setImageWidth() {
        if let image = cellImageView.image {
            let scale = image.size.height / contentView.frame.size.height
            var newWidth = image.size.width / scale
            if newWidth > maxImageWidth {
                newWidth = maxImageWidth
            }
    
            contentView.layoutIfNeeded()
            let animator = UIViewPropertyAnimator.init(duration: 0.1, curve: .easeOut) {[weak self] in
                guard let self = self else {return}
                self.cellImageViewWidthLayoutConstraint.constant = newWidth
                self.contentView.layoutIfNeeded()
            }
    
            animator.startAnimation()
        }
    }
    
  3. Next in the view controller, add the following to handle the change in width of the cell.
    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
        if let visibleCells = tableView.visibleCells as? [ExampleTableViewCell] {
            for cell in visibleCells {
                cell.cellWidth = tableView.frame.width
            }
        }
        super.willTransition(to: newCollection, with: coordinator)
    }
    

Believe it or not, I think that’s it! I’ve spent at least 4 weeks on this issue and keep running into some problem. Rotation and changing font sizes (Accessibility Inspector is great for testing) kept bringing up issues.

The example repo can be found here: https://github.com/sgruby/TableViewExample.

Feedback/changes are welcome! I’m sure I’m not doing something correct or there is an easier way; I just haven’t figured it out, yet.

Home Assistant and Node-RED, automation perfected?

Earlier this year I started experimenting with Home Assistant and wrote some thoughts about it. It brought together enough pieces that I keep tinkering with it. A friend of mine had mentioned a project called Node-RED which is supposed to more easily link together IoT components and perform actions based on different inputs. At the time, I brushed it off as I didn’t want to bother figuring out how to install it.

Fast forward to last week when I noticed that Home Assistant had a Node-RED add-on. Installing the add-on was quite easy and I was presented with a blank canvas. After a few minutes, I figured out how to make a simple sequence that took the state of a door sensor (at the time connected via my Vera) and turned on a light. Nothing too fancy, but I was able to hook it up, hit Deploy and test. It worked! This was light years ahead of the YAML based automations in Home Assistant and much faster to setup than Vera. I was hooked almost immediately. Could I convert all my automation logic to this? I spent the next few days trying.

Wiring some basic automations was quite easy as in this example that turns off lights in my bathroom if there is no motion. Node-RED resets the timer if there is more motion, making the sequence very straightforward.
Basic Node-RED Sequence

Unfortunately not all my automations are this simple.

The most important sequences deal with my front and back motion sensors. I could just use something like the above sequence, but I only want the lights on at night, I want to be able to disable the motion sensors (for example on Halloween if we’re not home, I don’t want lights coming on), and if I turn on the lights manually I don’t want them turning off a few minutes after there is motion. The tricky part here was to determine if the lights were manually turned on or triggered by a motion sensor. After a bit of experimenting, I decided to record the last time there was motion into a variable. Then when the lights turn on I check the variable to see how long ago it occurred. If it was less than 30 seconds ago, call it triggered my motion. With that, I was able to set on off timer based on how the lights came on. The one bit of “code” I had to write was to determine how long ago the motion was tripped.

    var motionTimestamp = flow.get('lastFrontMotion') || -1;
    var difference = (Date.now() - motionTimestamp) / 1000;
    if (motionTimestamp === -1)
    {
        difference = -1
    }
    msg.payload = difference;
    return msg;

The flow may look complicated, but to me it is quite readable.

Complicated Node-RED Flow

The visual aspect of Node-RED makes it easier to setup automations, but calling automation simple is far from the truth as I’ve shown above. As a professional software developer, writing code doesn’t scare me but for a hobby, this visual approach (with a little code as necessary) is much nicer. When I come back to this in 6 months, I have no doubt that I can read what is going on and troubleshoot as necessary.

For the last 5 years, I’ve been using my Vera to control everything using a plugin called PLEG which stands for Program Logic Event Generator. It has worked quite well, but it has been so long since I setup most of the automations, I have no idea what I did. PLEG, while functional, was a bit difficult for me to wrap my head around and it pained me every time I had to touch it. Also, when I touched it, I seem to recall having to restart Vera and wait only to find out that I needed to change something.

I’m so impressed with Node-RED that I’ve decided to see if I can move all my Z-Wave devices to Home Assistant using an Aeotec Z-Stick Gen5 plugged into the Home Assistant. The goal with this move is to speed up messaging; right now the Home Assistant polls (I think) Vera all the time looking for changes. This isn’t very efficient and pressing a button can take a second or two to have the message reach Home Assistant. Will this work? I hope so!

I know that I’m just scratching the surface with this, but I am very excited over the prospects!

Non-secure network connections in Carnival Cruise’s app

This past summer my family took a cruise on Carnival Cruise Lines to the Eastern Caribbean. There were a total of 17 of us and we had a good time. One of the suggested ways for everyone to stay in touch was to use the Carnival Hub App which is basically their goto app for up to date information on the ship which has a messaging component. For $5 per device for the cruise, it didn’t seem all that unreasonable except that just about everything on the cruise costs extra!

The chat app, like most chat apps, has push notifications. In iOS, there are 2 types of push notifications, local and remote. The remote ones require a persistent connection to Apple’s Push Notification Service (APNS). I suspected that the app used local notifications and stayed open in the background as having several thousand devices connected to either Apple or Google’s push servers over a satellite link would not make much sense. So I pulled out my trusty copy of Charles Proxy and decided to see what traffic was being sent. What I saw just about shocked me.

Connections using the app were NOT using SSL! Since the WiFi was unprotected (it would be cumbersome to give out the WiFI password to so many users), anyone with rudimentary hardware/software could sniff all the traffic. SSL certificates are cheap and easy to deploy, so there is no excuse for every service not to be using them (I use them internally on all services running at my house).

Is it so bad that the app isn’t using SSL as no credit card data is flowing through the app? Absolutely! People could be chatting about which rooms they are in and when they are going to meet giving criminals information about when to go into their rooms. People could also tell their friends/family what they have in their rooms making them targets for criminals (“I put the laptop/camera under the bed”, for example). Not only was chat not SSL protected, all other aspects of the app’s communication were sent in clear text.

Example requests and responses

This request has my Folio number and name; those 2 pieces of information could allow anyone to charge to my room. While they should look at the ship ID (you are given basically a name badge that is your room key and used for purchases), I don’t know if the staff always looked at them. My cabin number was also in the request.

GET /FHMA-leviathan/api/Guest?isKiosk=false HTTP/1.1

{
    "ChatPassword": "efabb219324c47dfbfef469523b495d0",
    "Nickname": "SCOTT GRUBY",
    "DiningRoom": "Northern Lights Upr",
    "DiningTime": "E",
    "DiningTable": "494",
    "MusterStation": "B4",
    "LoyaltyNumber": "XXXXXXX",
    "LoyaltyLevel": "BLUE",
    "NumCruises": "1",
    "DateOfBirth": "",
    "Age": "45",
    "BookingNumber": "XXXXX",
    "BookingSequenceNumber": "18",
    "FolioNumber": "8540",
    "FolioActiveIndicator": "A",
    "FolioType": "G",
    "FolioAccountNum": "8829",
    "ResponsibleParty": "Y",
    "AlcoholRestricted": "N",
    "AtRisk": "",
    "CashBalance": "85.25",
    "AccountType": "MIXED",
    "FolioLimit": "",
    "TotalCharges": "43.66",
    "CabinNumber": "2309",
    "CrewInfoNumber": "",
    "VoyageStartDate": "2018-08-04T10:01:55",
    "VoyageEndDate": "2018-08-11T10:01:55",
    "Duration": "7",
    "FacebookId": null,
    "InitialLogin": "2018-08-04T14:08:02.963",
    "ChatPurchased": "2018-08-04T14:10:15.9",
    "InitialUserAgent": "funhub/2587 CFNetwork/902.2 Darwin/17.7.0",
    "IsResponsibleParty": true,
    "IsFolioCancelled": false,
    "IsFolioDeactivated": false,
    "FirstName": "SCOTT",
    "LastName": "GRUBY",
    "VoyageId": "MC20180804007",
    "AvatarUrl": "http://leviathan.cclfunhub.com/FHMA-leviathan/Avatars/2811.jpg?636690028868502174",
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": false,
    "ChatId": 2811,
    "SelfieUrl": null,
    "DecurtisGuestId": "445911",
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": true
}

Want to know who I have on my chat list? Bingo! (Names were removed.)

GET /FHMA-leviathan/api/contacts HTTP/1.1

[{
    "Relationship": "Chat contact request",
    "IsMinor": false,
    "FirstName": "XXXXXXX",
    "LastName": "XXXXXXX",
    "VoyageId": "MC20180804007",
    "AvatarUrl": "http://leviathan.cclfunhub.com/FHMA-leviathan/Avatars/1248.jpg?636690701830901659",
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": null,
    "ChatId": 1248,
    "SelfieUrl": null,
    "DecurtisGuestId": null,
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": null
}, {
    "Relationship": "Chat contact request",
    "IsMinor": false,
    "FirstName": "XXXXXXX",
    "LastName": "XXXXXXX",
    "VoyageId": "MC20180804007",
    "AvatarUrl": "http://leviathan.cclfunhub.com/FHMA-leviathan/Avatars/2074.jpg?636690009457720910",
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": null,
    "ChatId": 2074,
    "SelfieUrl": null,
    "DecurtisGuestId": null,
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": null
}, {
    "Relationship": "Chat contact request",
    "IsMinor": false,
    "FirstName": "XXXXXXX",
    "LastName": "XXXXXXX",
    "VoyageId": "MC20180804007",
    "AvatarUrl": "http://leviathan.cclfunhub.com/FHMA-leviathan/Avatars/2075.jpg?636690046360212793",
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": null,
    "ChatId": 2075,
    "SelfieUrl": null,
    "DecurtisGuestId": null,
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": null
}, {
    "Relationship": "Chat contact request",
    "IsMinor": false,
    "FirstName": "XXXXXXX",
    "LastName": "XXXXXXX",
    "VoyageId": "MC20180804007",
    "AvatarUrl": null,
    "IsChatProvisioned": true,
    "IsChatPurchased": true,
    "AcceptedPixelsTerms": null,
    "ChatId": 2396,
    "SelfieUrl": null,
    "DecurtisGuestId": null,
    "DismissedPixelsSurvey": null,
    "CompletedPixelsSurvey": null
}]

Want to know what my room charges were?

GET /FHMA-leviathan/api/guest/AccountSummary HTTP/1.1

{
    "TotalCharges": "346.17",
    "TotalCash": "0",
    "TotalCredits": "60.92",
    "BalanceDue": "0",
    "AvailableCash": "0",
    "AvailableCashAtFolio": "0",
    "MaxCashForDeposit": "9999",
    "CashBalance": "85.25",
    "AccountType": "MIXED",
    "GuestList": [{
        "GuestCharges": "134.31",
        "GuestChargesLessGrats": "43.66",
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "FolioNumber": "8540",
        "VoyageNumber": "MC20180804007",
        "BookingNumber": "8GM8F5",
        "PaxSeqNumber": "18"
    }, {
        "GuestCharges": "91.65",
        "GuestChargesLessGrats": "1",
        "LastName": "GRUBY",
        "FirstName": "XXXXXX",
        "MiddleName": "XXXXXX",
        "FolioNumber": "8538",
        "VoyageNumber": "MC20180804007",
        "BookingNumber": "8GM8F5",
        "PaxSeqNumber": "20"
    }, {
        "GuestCharges": "120.21",
        "GuestChargesLessGrats": "29.56",
        "LastName": "GRUBY",
        "FirstName": "XXXXXXX",
        "MiddleName": "XXXXX",
        "FolioNumber": "8539",
        "VoyageNumber": "MC20180804007",
        "BookingNumber": "8GM8F5",
        "PaxSeqNumber": "19"
    }],
    "FolioCharges": [{
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "11:28:17AM",
        "ChargeLocation": "INTERNET ACCESS",
        "ReceiptNumber": "379101",
        "ChargedAmount": "0",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "11:40:45AM",
        "ChargeLocation": "NON-REFUNDABLE ONBOARD CREDITS",
        "ReceiptNumber": "020620",
        "ChargedAmount": "-25",
        "IsReceiptAvailable": "N"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "11:40:46AM",
        "ChargeLocation": "REFUNDABLE ONBOARD CREDITS",
        "ReceiptNumber": "020620",
        "ChargedAmount": "-3.64",
        "IsReceiptAvailable": "N"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "2:14:49PM",
        "ChargeLocation": "CHAT PLAN",
        "ReceiptNumber": "381964",
        "ChargedAmount": "5",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/4/2018",
        "ChargeTime": "2:51:38PM",
        "ChargeLocation": "BLUE IGUANA BAR",
        "ReceiptNumber": "382739",
        "ChargedAmount": "10.07",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/6/2018",
        "ChargeTime": "6:18:33PM",
        "ChargeLocation": "NORTHRN LS UPPER SVC served in Main Dining Room",
        "ReceiptNumber": "419829",
        "ChargedAmount": "10.64",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/7/2018",
        "ChargeTime": "8:37:19AM",
        "ChargeLocation": "LAUNDRY SELF-SERVICE",
        "ReceiptNumber": "429017",
        "ChargedAmount": "3",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/7/2018",
        "ChargeTime": "11:31:37AM",
        "ChargeLocation": "TOWELS,ROBES&BOOKS",
        "ReceiptNumber": "430321",
        "ChargedAmount": "14.95",
        "IsReceiptAvailable": "Y"
    }, {
        "LastName": "GRUBY",
        "FirstName": "SCOTT",
        "MiddleName": "ALLEN",
        "ChargeDate": "8/9/2018",
        "ChargeTime": "2:04:26PM",
        "ChargeLocation": "SERVICE GRATUITY",
        "ReceiptNumber": "876839",
        "ChargedAmount": "90.65",
        "IsReceiptAvailable": "Y"
    }]
}

It appears that the chat application was using the Jabber protocol (XMPP) running on port 5222 (non SSL port). I only grabbed a little of the traffic, but the XMPP protocol would have all the chat conversations appearing in it.

<stream:stream xmlns:stream='http://etherx.jabber.org/streams'
xmlns='jabber:client' xml:lang='en-US.UTF-8' id='1B9C3F49DB9F71'
from='chat.cclfunhub.com'
version='1.0'><stream:features><mechanisms
xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>PLAIN</
mechanism><mechanism>CISCO-VTG-TOKEN</mechanism></mechanisms></
stream:features><success
xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/><stream:stream
xmlns:stream='http://etherx.jabber.org/streams'
xmlns='jabber:client' xml:lang='en-US.UTF-8' id='1B9C3F49DB9F71'
from='chat.cclfunhub.com' version='1.0'><stream:features><bind
xmlns='urn:ietf:params:xml:ns:xmpp-bind'/><session
xmlns='urn:ietf:params:xml:ns:xmpp-session'/></stream:features><
iq id='0E579D08-88CC-4A8E-8A80-C730BA9B0505' type='result'><bind
xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>2811@chat.
cclfunhub.com/FA8CEBAF-6EDF-4B10-8D1E-7E6AF213F07C</jid></bind><
/iq><iq id='63D67AE8-2A53-4047-A9BE-A3E429953B7E'
type='result'/><presence
from='505c1c79-24c3-41d6-89c3-31e687ad3ab4@conference-2-
standalonecluster61f84.chat.cclfunhub.com/3940#ad29759f-2e4d-
4428-bbd2-23f953c285d2' id='tlDWA-7797'
to='2811@chat.cclfunhub.com/FA8CEBAF-6EDF-4B10-8D1E-7E6AF213F07C
'><x xmlns='http://jabber.org/protocol/muc#user'><item
affiliation='owner'
jid='3940@chat.cclfunhub.com/8f1c3bd92d91ad29'
role='moderator'/></x><c hash='sha-1'
node='http://www.igniterealtime.org/projects/smack'
ver='A1TcJY4mFaFrO9M5ctJsIPdHhsU='
xmlns='http://jabber.org/protocol/caps'/><delay
stamp='2018-08-10T04:05:07.36362Z' xmlns='urn:xmpp:delay'/><x
stamp='20180810T04:05:07.36362'
xmlns='jabber:x:delay'/></presence><presence
from='505c1c79-24c3-41d6-89c3-31e687ad3ab4@conference-2-
standalonecluster61f84.chat.cclfunhub.com/2508#4faf58be-ee9d-
4d80-8ee2-2ee662f6629a' id='vj32Z-86255'
to='2811@chat.cclfunhub.com/FA8CEBAF-6EDF-4B10-8D1E-7E6AF213F07C
'><x xmlns='http://jabber.org/protocol/muc#user'><item
affiliation='owner'
jid='2508@chat.cclfunhub.com/887e88d21b5f8201'
role='moderator'/></x><c hash='sha-1'
node='http://www.igniterealtime.org/projects/smack'
ver='A1TcJY4mFaFrO9M5ctJsIPdHhsU='
xmlns='http://jabber.org/protocol/caps'/><delay
stamp='2018-08-10T03:02:13.862187Z' xmlns='urn:xmpp:delay'/><x
stamp='20180810T03:02:13.862187'
xmlns='jabber:x:delay'/></presence><presence
from='505c1c79-24c3-41d6-89c3-31e687ad3ab4@conference-2-
standalonecluster61f84.chat.cclfunhub.com/1248#-iOS-00689BAE-
CC73-43A3-A258-4E9F8BA160A2'
to='2811@chat.cclfunhub.com/FA8CEBAF-6EDF-4B10-8D1E-7E6AF213F07C
'><x xmlns='http://jabber.org/protocol/muc#user'><history
maxchars='0'/><item affiliation='owner'
jid='1248@chat.cclfunhub.com/E22C11CA-B399-4D9B-A3D9-
BE9C35B5A1DD' role='moderator'/></x><delay
stamp='2018-08-10T03:57:54.660208Z' xmlns='urn:xmpp:delay'/><x
stamp='20180810T03:57:54.660208'
xmlns='jabber:x:delay'/></presence>

WiFi Issues

The security of the app wasn’t the only issue in using it. I suspect that the WiFi was also overloaded; with something like 6000 people on the ship, there were several thousand devices connected at all times even if they weren’t communicating at the same time. This made using the app very frustrating; messages didn’t get through and notifications were delayed, if they were received at all. Imagine all the people annoyed that his or her significant other wouldn’t respond even though he or she actually did. I stopped relying on the app in the first few hours of using it!

Recommendations to Carnival

  • Deploy a wildcard SSL certificate that is issued by a major SSL vendor (no self-signed certificates) to each internal server on each ship.
  • Add more WiFi capacity to every ship.
  • Perform load testing of the apps (iOS and Android).
  • Perform load testing of the WiFi network.

I’m not sure if the app has been updated, but here was the information about the version:

System Name: iOS
System Version: 11.4.1
Application Version: 2.2.3 (Build 0)
Ship Name: Magic
Hostname: http://leviathan.cclfunhub.com/FHMA-leviathan
Voyage ID: MC20180804007

Conclusion

While the concept of being able to communicate with others on a cruise especially if you have a large party is great, Carnival’s implementation needs work. In the future, I’m inclined to bring FRS radios; they definitely won’t work everywhere, but could be more reliable in certain situations. Also, meeting your group each morning and going over plans even if people go their separate ways (like people did before technology!) might also be in the cards.

If Carnival wants to get in touch with me about these issues or wants help with the app, I’m available!

HDMI ARC and HDMI CEC

Several years ago, I purchased a Vizio 5.1 soundbar system. At the time, the way to get the best audio from it was to use the optical input. This worked fine, but required me to use 3 remotes for watching TV; 1 for the TV, 1 for the soundbar, and 1 more for the Roku I had at the time. When the Apple TV 4 came out, I learned about HDMI CEC which is basically a protocol that lets devices talk to each other and have some control. The Apple TV remote then let me turn on the TV and put it in standby without touching the TV remote. That brought me down to 2 remotes. The Apple TV remote could also control the soundbar using IR which brought me down to 1 remote.

This setup worked fine for years, but had a few slight problems. The first is that when I powered on the Apple TV and TV using the remote, I’d have to hit the volume up button a few times to wake up the soundbar and then would have to lower the volume. Second is that putting the Apple TV and TV in standby did nothing for the soundbar; it went into low power mode after awhile, however. The last complaint, albeit minor is that I couldn’t use my iPhone or iPad to control the volume.

I’d read about HDMI Audio Return Channel (ARC) where instead of using optical audio out, an HDMI cable could be used which would give better audio. My soundbar didn’t have this option (the TV which was older than the soundbar did have it) so I was stuck with the optical audio. In addition, if the devices supported HDMI CEC, the volume could be controlled using another device’s remote.

A few weeks ago, I finally decided to upgrade my soundbar to one that supports Dolby Atmos and purchased the Vizio SB36512-F6 which was on sale at Costco. While I have no idea if I’ll be able to hear the Dolby Atmos (I need content to support it), I’m pretty pleased with the purchase. This soundbar is connected via HDMI and allows me to use the Apple TV remote (and my iPhone/iPad) to completely control my entertainment devices. In addition, the sound on the bar seems crisper and can now hear the rear speakers much better. It may be that HDMI ARC works better than optical or maybe makes it easier to configure. I am excited to be able to try out Atmos and see if that lives up to the hype in the room I watch TV (it may not as the ceiling isn’t that high and due to the layout, it is just part of a larger room).

I love when devices work together and with this new soundbar, I may have found the perfect combination for my viewing experience.

One last thing, the iPhone app for the soundbar is a piece of garbage. I used it to upgrade the firmware on the soundbar and promptly deleted it. Why is is so hard to make a basic app for controlling the settings of a device?

Honey Mustard Kebab Recipe

I’m not a great cook, but in order to help out my wife I’ve been cooking at least 2 days a week for the last year or so. I’ve found a few recipes that work and pretty much stick to them. Yes, this is quite boring but it works for us. However, when I ask my wife if she likes it, she says she likes not cooking those nights which isn’t a big encouragement! Last week I got the idea that I was going to make kebabs and when I went to the store to get ingredients, a bottle of brown mustard called my name.

I went searching for a recipe that had brown mustard in it that met my dietary needs and didn’t require me to go back to the store. Unfortunately there wasn’t an easy way to take an inventory of what we had and get a recipe, so my searching came up with a recipe that was kind of in the ballpark of what I wanted. I don’t experiment in the kitchen and tend to follow recipes, but I decided to go out on a limb and make some modifications. When I cooked the chicken the following night, my wife said it was excellent which was a huge relief to me! I guess I’m starting to understand what ingredients make what flavors and what we like. Who knows, I might take a cooking class one day!

In any case, for my own records here is the recipe I ended up using:

Ingredients

  • 1/4 cup honey
  • 4 tablespoons sweet brown mustard (no added sugar)
  • 1 tablespoon coconut aminos
  • 1 tablespoon coconut sugar
  • 1 tsp parsley flakes
  • 1 tsp paprika
  • 1 tsp salt
  • 1 tsp minced garlic (we have a big container from Costco)
  • 1/4 teaspoon black pepper
  • 1/8 teaspoon cayenne pepper
  • 3 tablespoons olive oil
  • 1/2 green bell pepper
  • 1/2 red bell pepper
  • Other vegetables to grill
  • 1 to 1.5 lbs chicken

Directions

  1. Combine all ingredients (except for the vegetables and chicken) in a small metal bowl.
  2. Whisk ingredients.
  3. Cut chicken into chunks.
  4. Cut peppers and other vegetables into chunks
  5. Put chicken and peppers in quick marinator (not sure this really does anything, but I did it anyway).
  6. Pour marinade over chicken and peppers.
  7. Completely cover everything with marinade.
  8. Refrigerate for 24 hours.
  9. Place chunks on metal skewers.
  10. Grill to perfection!

Enjoy!

Review: Anker PowerCore 26800 Power Bank

On a recent camping trip, I brought along a few small power banks to charge phones and watches (we weren’t completely out in the wilderness and having a phone for pictures and emergencies is quite important). It was kind of awkward to charge the devices using 3 separate batteries. I decided to look for larger power banks that could charge multiple devices at once. Originally I was looking for one that could also power my laptop for a little bit, but decided that the number of times I’ve had to power it have been few and far between. Since I had good results with Anker products, I purchased the Anker PowerCore 26800 Portable Charger.

This charger is close to the largest battery that you can legally bring on an airplane, but weighs just over a pound. In addition to the 3 USB ports (many power banks just have 2), it has 2 micro USB ports for input to charge it faster. With a battery this large, faster charging is nice to have. The power bank is pretty basic; charge it up with the micro USB inputs (it doesn’t come with a wall adapter, so I just use the Anker 6-Port USB Charger to charge it) and then plug devices into the USB ports.

We used the power bank a number of times this past summer during another trip. I put it in my backpack with a few cables and during the day charged up our phones (even with new batteries, our iPhones suck down batteries when using GPS). Being able to plug in 2 devices at once was quite convenient. Also since the battery is so large, I think I only had to charge it once on a 2 week trip.

This battery has performed well and I anticipate using it on camping trips, family vacations, and having it around in case of emergencies. As I’ve switched to USB charging for as many devices as possible including flashlights, this battery can also be quite helpful in an emergency or a disaster.

Pros

  • Largest battery you can take on a plane.
  • 3 USB ports for charging devices.
  • Faster charging with 2 USB inputs.
  • Not too heavy.

Cons

  • Some may consider it a bit expensive for a battery.

Summary

This power bank has allowed me to consolidate power banks on trips. While it may not be the smallest, I don’t have to worry about it running out of power even if I charge a number of devices. It just works and the 3 USB ports make it more useful to me than other power banks I could have purchased. If you’re in the market for a power bank, I’d definitely consider this one.

Never enough charging ports

This past summer my family went on a vacation to the Grand Canyon and a few other destinations in Arizona (yes, it was hot!). For the driving trip we packed a lot of devices. Between the 3 of us, we had 3 iPads, 3 iPhones (my son just uses one for its camera), 2 Apple Watches, AirPods, a mobile hotspot, and an Anker PowerCore 26800 Power Bank. After I bought and reviewed the Anker 5-Port USB charger several years ago, I realized that the Anker 6-Port USB Charger was a better device as it didn’t add much bulk to the 5 port and gave me an extra port. Since then I’ve bought a few of the chargers and put one along with cables in a small bag I take when I travel. While I don’t need to charge everything at once, 6 ports just isn’t enough to keep everything charged especially since the power bank uses 2 ports to charge faster. After we got back from the trip, I ordered another charger.

With 2 6-port chargers, I think that I’m all set for awhile. If I plug in everything at once (which I’ll rarely do), all the devices will use 12 ports! That’s kind of sad and amazing at the same time that we travel with some much technology. I just have to remember to bring enough cables to charge what needs to be charged. In my opinion, there is no reason to ever travel with smaller chargers even if they are a little more compact. I travel alone, I’m going to have 6 devices with me. Yes, I’m addicted to my gadgets.

Porting an iOS app to macOS

About six weeks ago (2 weeks or so before WWDC), my client asked me to port an enterprise app I wrote for iOS to macOS. I haven’t done macOS work for a long time, but how hard could it be? In the last few years, a number of iOS-like technologies have come to macOS; while they aren’t named the same, many things function similarly like NSViewController (UIViewController), NSTableView (UITableView), NSTableCellView (UITableViewCell), etc. All of my iOS apps for this client are written in Swift, so it made a lot of sense to use Swift for this macOS app.

Getting started with the project took about a week to get familiar with macOS again, but then things started moving. The first thing I did after the app ran was to make a version of my framework that I use across 5 iOS apps (models, networking, methods, etc.) over to the Mac which wasn’t difficult; I only had to do a few platform specific defines for the files I moved over (I didn’t move the UI pieces over). Once the basic app was running, I started the UI and had real data showing up within a few weeks from start. I took a number of pieces of the iOS app, copied the code and pasted it into the Mac app. The number of changes for these pieces were minimal (.stringValue instead of .text on the NSTextField vs UILabel), but I was quite pleased how I was able to reuse the code.

From start to basically feature parity with iOS took about 5 weeks. I’m sure that there are things that I’d change such as doing extensions on classes instead of copying/pasting code as I’ll have to maintain both apps going forward, but that could obscure how things work. I am extremely pleased with how well this project is going (it hasn’t been deployed, yet).

At WWDC Marzipan was revealed and it looks like it will allow many iOS apps to run on macOS. This, of course, would have helped me get my app up and running, but would it feel like a Mac app? While not every app is as straight forward as the one I ported, developers that want to move their apps to macOS today have nothing stopping them.

Review: RTSystems ID-51APlus2 Programmer for Mac

[Update 04/21/17: I did receive an email back from the company on Tuesday wanting to assist me; unfortunately this message went to spam. Their support is definitely responsive and if I implied that they weren’t, I do apologize.]

When I purchased my Icom IC-7100, I knew that I’d need software to program it as entering information for hundreds of memory locations would be impossible. There were 3 choices; one was expensive from ICOM, a free one (CHIRP) and the one that people seem to rave about from RT Systems. I had read that CHIRP didn’t work properly with the radio, so I went with the one from RT Systems. I bought the Windows version as no Mac version currently exists for it. The software was adequate, but nothing I’d write home about.

Since I wanted to use the same software (or similar) to program my new Icom ID-51A Plus2, I went with RT System’s new Mac version of the software.

The first thing that I had to get over with the software is that there is a different version for just about every radio. The cost for each radio didn’t bother me ($25 per radio); it was how the software was packaged. I would have expected to purchase one application and then pay a fee to unlock different radios. Instead, the company/author has chosen to do one application for each radio. Having used the software for two different radios, I can sort of see the reasoning as each radio has different options and some just don’t overlap. However, I still believe that having plugins would be a better experience for the user.

The second thing that annoys me a little is that I have to purchase a cable from the company. The IC-7100 is an exception as it has a USB port and the company supports it directly. My ID-51APlus2 came with a USB cable, but it won’t work with the software. I can understand that for radios that don’t come with a cable the company doesn’t want to have to support cables with crappy USB to serial chips, but if the cable comes from the manufacturer, they should support it.

I installed the software last week and was underwhelmed by the experience. The application is a Java application complete with a Java installer. I use a Macintosh for many reasons, among them is that I like the user interface and cross platform user interfaces just look bad. I received the cable last Saturday but didn’t get a chance to play with the software until Monday. I plugged in the cable and tried reading data from the radio, but the software said it couldn’t find the cable. I moved the cable to a different USB port (I tried with a USB-C to USB-A Apple dongle, a USB-C to 4 port USB-A hub, and a USB port on my Thunderbolt Display) with no change. I saw that there was a /dev/tty. port registered and the cable showed up in System Profiler indicating that the driver loaded properly. I sent email to the company and didn’t hear back. Yesterday I called the company and was transferred to Rob in technical support (Rob is also the lead developer; maybe Rob is the R in RT Systems). He wanted to connect to my computer using Team Viewer, but I declined and gave him all the information he needed (in a past life I wrote USB to serial drivers so I know my way around drivers). After a few minutes, Rob said that this particular cable seems to give them problems and he told me to check for updates. I did that and after installing it, the software saw the cable. Excellent! I hung up and let the software finish reading from the radio. Unfortunately the progress bar never moved, but the radio thought it was done. I called again and talked to a different person in support. The woman gathered information, then chatted with Rob and said that they were able to reproduce the problem. She said she’d call back when an update was available. Later that afternoon, I checked for updates and was finally able to read from the radio. (I did receive a callback this morning telling me about the update.)

Now that I was able to actually use the software, I exported memory locations from my IC-7100 (on Windows) in CSV format and imported them into the ID-51APlus2 programmer. Given that the software is made by the same company, I would have expected an easier way to transfer ALL the memory locations (I had to transfer 1 bank at a time) to a new radio. In addition, the software uses non-standard open and save dialog boxes which don’t allow dragging and dropping of files adding some hoops to import the memory banks. I wrote the data to the radio and that worked fine. As I worked on adding memory locations, I realized I wanted to move a bunch of locations to a “memory bank”. Normally I’d expect to be able to select a bunch of locations and change them all at once. Unfortunately that didn’t work and I had to go through roughly 200 memory locations one by one to change the bank.

I am impressed that the software covers lots of settings on the radio, some that I haven’t even discovered yet. This software is functional, but is definitely not a joy to use. Luckily it isn’t a program that is used daily.

Main Screen
Settings

Pros

  • Allows you to easily configure all features of the radio including memory banks and settings.
  • Allows importing and exporting of memory locations to move data between radios.
  • It works on a Mac.
  • Extremely responsive telephone support.

Cons

  • The user interface is just awful. There is no way to sugar coat this.
  • Non-standard user interface. The tabs aren’t Mac tabs. There are indicators for CAP NUM and SCRL which don’t do anything or affect current state.
  • No tooltips.
  • No way to change one option for multiple memory locations at once. For instance, if I want to mark a bunch of memories to skip or move them to a bank, I can’t.
  • There is no undo.
  • It is unpolished; I can seize a modal dialog box and various controls overlap.
  • Doesn’t work with the cable that comes with the ID-51APlus2 radio.
  • Initial problems getting started which required 2 updates to work.

Summary

I’m not sure if HAM radio operators are just happy to have any software or if they truly have not used good looking software, but so far I’ve only seen one application that looks like a quality piece of software. The RT Systems programmer for the Mac is barely usable as are most cross platform programs, in my opinion. If the software had cost a little bit more in order to cover the costs of a really polished program, I don’t think I’d hesitate to purchase it. Aether costs $40 and I didn’t hesitate to purchase it because it was heads above the competition in design and usability.

If you’re a Mac user and need to program a radio, CHIRP might work, but you’ll find that the RT Systems software does more. You really don’t have a choice in the matter besides using Windows and then you’d use the Windows version of the software. I understand that the market to sell the software is quite limited and dying off (literally), but I really expect more when I purchase software. I can’t recall an application on my Mac that has a worse user interface.

As much as it pains me to do so, I have to recommend this software as the easiest way to program the ID-51APlus2 radio and probably other HAM radios.