iZone or myzone3 integration

Discussion in 'General Discussion' started by paul.parisi, Jul 30, 2017.

  1. paul.parisi

    paul.parisi

    Joined:
    Sep 28, 2005
    Messages:
    14
    Likes Received:
    0
    Location:
    Melbourne
    Hi all
    this may be a little off topic but I'm working on extending my own cbus automation and wanted to improve the overall HVAC control I have.

    I'm working with a 10 year old Hitachi Utopia system (8 on-off zones controlled by manual IAS wall controllers) which could do little more than allow cbus to turn it on or off and detection run state using relays and some cbus logic. Note I did also setup a IR interface but never got enough detail out off the hitachi techs to figure out how to use the PAC or anything else to control it via IR.

    As one of the wall controllers had failed I started looking into this again had noticed the iZone (airsteam) and myZone3 (metalflex) systems looking for replacement parts with the idea of "upgrading" in mind.

    Apparently they "claim" to be "add on" automation control systems that can upgrade older systems and provide an automation interface (rest api) and also control the old on/off zone motors like a VAV system would, making ducted systems much more efficient.

    Note from what I can tell, they both are based on the same hardware but were branched at some point. Myzone3 seems to be more targeted to older equipment as the vendor claims it can control IAS zone motors (as if they were VAV motors using timing information) and also the older Hitachi's by the A/B cable to give full control better than I have with cbus now. No guarantees just claims currently, apparently myzone3's aren't regularly installed in Victoria (my state) but QLD.

    If I went this way I would rewrite PAC logic and the HVAC control logic using a framework like openhab2.

    That said, apart from a scrappy manual with very little information I cannot find much on either system and the vendors themselves aren't that very familiar with it technically. An add on system seems to cost somewhere between 1k-3k in parts depending on what you do (e.g full control, temp sensors, or just zone control).

    Wondering if anyone has experience with them and could share there thoughts? Stay clear? Better options? Great experience?

    Do I stick with the wall controllers or take a gamble on this. If I gamble with it I'd build an openhab plugin in the process.
     
    paul.parisi, Jul 30, 2017
    #1
  2. paul.parisi

    DarylMc

    Joined:
    Mar 24, 2006
    Messages:
    1,308
    Likes Received:
    49
    Location:
    Cleveland, QLD, Australia
    Previously I have used IAS Smartzone to control some zone dampers.
    It is a VAV system with individual room temp controllers and uses a time calculated position system to modulate the zone.

    I was able to get someone to integrate to Colour touch screen with some logic via ethernet socket, a serial device server and an RS485 interface on the Smartzone.
    At AC startup logic would open all the zones.
    Never did get around to integrating the room temps to CBus but they could be set individually on the Smartzone controller or remotely through the RS485 interface.

    For the AC units I used a product called Coolmaster D to interface to the Daikin AC central control lines.
    They do other brands and I think Hitachi is one.
    The logic to run that was also on Clipsal Colour touch screen.

    There is a company in Brisbane called Insight Control.
    They should be able to give good info about the IAS zone products and possibly your existing system.
     
    Last edited by a moderator: Jul 31, 2017
    DarylMc, Jul 30, 2017
    #2
  3. paul.parisi

    daniel C-Busser Moderator

    Joined:
    Jul 26, 2004
    Messages:
    766
    Likes Received:
    20
    Location:
    Adelaide
    I wanted an API for my new A/C and ended up choosing the Airstream iZone 325 system. It appears to be a cost-effective option for residential - it's not drastically far off the price of the manufacturers' own controllers. There is an ethernet port tucked away on the wifi module and the company gives out the API on request. From a cursory glance it appears to cover all the automation commands one would typically need from zone control to retrieving current temperatures, but as my system won't be installed for a few months I haven't had a chance to actually code against the API yet. How well it actually works remains to be seen!! I haven't found any plugins or projects using the API so I'm expecting to write my own logic.

    The AdvantageAir MyZone 5 controller and Actron's new Que controllers didn't have APIs when I asked, nor did several other manufacturers. I hadn't heard of Metalflex myZone3 til now.

    I've since learned about CoolMaster as an option with its history of C-Bus integrations, and that integration modules for the Modbus standard are beginning to cross over from commercial HVAC to residential. There ends my paltry knowledge though.

    NB: Above opinions are my own and not those of Clipsal or Schneider Electric.
     
    Last edited by a moderator: Jul 31, 2017
    daniel, Jul 31, 2017
    #3
  4. paul.parisi

    DarylMc

    Joined:
    Mar 24, 2006
    Messages:
    1,308
    Likes Received:
    49
    Location:
    Cleveland, QLD, Australia
    Between the VAV zone system and full control of the AC units I had control of just about everything.
    In the end very little was ever implemented.
    Because you need to set the zone temps somewhere north or south of the AC setpoint depending on mode it just ended up making things quite complex for the customer.
    For example auto mode on the AC is not going to be a good idea without a bit of automation.

    Once the system was commissioned, because it was a fairly well planned layout and because the home didn't have people complaining they were freezing in some rooms but not others I didn't feel the need to pursue it.
    I've seen that scenario before and that is why I installed the VAV system.

    I'd look at individual AC units or a VRV system for every zone where temp was critical in the future.

    I was very worried 10 years ago to send a $1000+ cash transfer to a foreign country for a product very few people had used.
    Coolmaster interface has proved very reliable for me.
    Back then I had some blind faith I could figure out how to implement it too.
    That really wasn't the case.

    Many thanks to forum member djaggar for the logic to get it working.
    I could not have done it without his help.
     
    DarylMc, Jul 31, 2017
    #4
  5. paul.parisi

    paul.parisi

    Joined:
    Sep 28, 2005
    Messages:
    14
    Likes Received:
    0
    Location:
    Melbourne
    Hi DarylMc
    Thanks for the information! Actually I started the process looking for the IAS Smart Temp. I believe it was released to market a little while after I installed the system. Unfortunately I ran into a dead end finding it now. IAS has been acquired by someone else and after numerous calls I finally found out from Metalflex that IAS product lines such as the smart temp hadn't been sold in years now. Its ashame as it was a logical extension from what I can see.
    Metalflex claim the product lost popularity in automation due to issues with reliability in the serial interfacing. More likely automation experts found it too painful to setup is my guess.

    I can understand what you're saying about complexity of automating HVAC control. I think my vision at the time was just to manage fan speed and zones typical configurations, plus heating/cooling (yet to avoid using auto mode) and use just a temp threshold. Doing more than this would require some complex programming.

    the iZone and Myzone3 "claim" to have logic in them so all you do is set maximum opening/closing for each zone, set "area" of a room or install temp sensors and "in theory" can therefore run it efficiently so the automation interface would be more to manually override it or turn it on/off, etc.

    Looking at the Coolmaster D (which is now replaced apparently with another model) it look like an add on directly to single vendor HVAC (zoning not a third party) as the website doesn't show any third party zone examples. Did you get it controlling zone motors?? I'll have to look more into this.

    One thing the coolmaster product seems to consider is multiple HVAC systems and its field upgradeable which the izone/myzone3 doesn't' seem to do. So if I add a couple splits in the izone/myzone3 won't be aware of them.

    I'll reach out to insight control.
    thanks
    Paul
     
    paul.parisi, Aug 1, 2017
    #5
  6. paul.parisi

    paul.parisi

    Joined:
    Sep 28, 2005
    Messages:
    14
    Likes Received:
    0
    Location:
    Melbourne
    Thanks Daniel, ok that saves me researching AdvantageAir and Actron, without API control its pretty useless extending.

    Ashame you haven't installed yet!

    I have the API spec for the myzone3 but the dodgy thing is its labeled the "iZone Ethernet Interface v1.00" hence where i started to realise the products were based on similar tech. The API document is 18 pages, pretty brief for an API and doesn't cover basics like whether it handles secure connectivity or if its possible to field update the firmware. (I'm guessing no)

    The manual for the izone is here which is also almost identical to the myzone3 (which just has a section on hitachi and a few other things)

    https://www.airstreamlivingtechnolo...225-325-Rev-4-installaion-and-user-manual.pdf

    I'm curious to know if they had any other doco, images or information you could share if you have the equipment around, perhaps by direct message.

    Paul
     
    paul.parisi, Aug 1, 2017
    #6
  7. paul.parisi

    bmerrick

    Joined:
    Jun 13, 2007
    Messages:
    434
    Likes Received:
    34
    Location:
    Sydney
    Hi Paul,

    The other guys have given you some good advice there so I was reticent to jump into a post that was already well answered as I would have also probably suggested looking at the Coolmaster or CoolMasterNet for centralised A/Cs or iZone, but in your recent post you said:
    I recently completed a site that had a Hitachi ducted Aircon with a main zone and several smaller peripheral zones (kitchen , dining, study), but with upstairs bedrooms with split systems of two other vendors obviously installed sequentially over time.

    To integrate all these into a C-Bus network, I used the Airtopia solution (which is great value for the split systems C-Bus integration being their target market) for the splits, but also in a fairly creative way with the Hitachi ducted system. I sought an Infrared remote board for the Hitachi unit which was a only few hundred dollars (thanks TemperZone) and then made the Airtopia talk to it via IR just as if it was a normal split system. The Hitachi control IR codes were sent to Airtopia and I understand are now incorporated into their codeset database. This gave me On/Off, Fan Speed, A/C Mode (Heat, Cool, Dehumidify), Set Temp and Current Temp for the Hitachi ducted unit the same as for the other split systems. Being a well thought out C-Bus enabled partner device, the Airtopia acts just like a C-Bus thermostat and so you can use the Cool looking HVAC Wiser2 widget, works nicely with eDLTs to control, set and read temps etc and also the HVAC app links from the CTC and SP / Homegate. Temp reads on measurement app like a C-Bus temp sensor and power monitoring is also C-Bus compatible looking like a CMU.

    Zone damper controls which were previously on a third party damper single wall switch in a useless position at the bottom of some stairs was removed and the zone dampers just controlled through a normal C-Bus relay. Whilst the client only wanted to be able to turn the Hitachi ducted's peripheral zones on and off, this could have easily been a simple temp feedback control written in logic based on a C-Bus room temp sensor controlling the relay or through occupancy sensors.

    Anyway, like anything in HA, there are always many options to do anything and I just conveyed this as another possible solution as you mentioned adding splits.

    Cheers,

    Brad
     
    Last edited by a moderator: Aug 1, 2017
    bmerrick, Aug 1, 2017
    #7
  8. paul.parisi

    znelbok

    Joined:
    Aug 3, 2004
    Messages:
    1,151
    Likes Received:
    17
    I have an older advantage genIII system - 8 zones, every room is a zone. It has a home automation interface with a nice simple ascii protocol. Its ties in with CQC and so is C-bus. I dont use their touchscreen at all, I use C-bus switches in each room to turn the rooms on and off and the main touchscreen for temp setting etc. Its also tied to the fans so when a room is turned on if there is a fan it is also turned on.

    From my last talk with Advantage Air they were only dealing with Qld and maybe WA - not in NSW or Vic any more. I hope they have not removed an option for HA integration - they were very keen on it when I bought there system.

    Coolmaster is probably the best bet as its protocol is easy as well and can be used with a number of systems. i actually wrote a driver for it for CQC but have not commissioned it yet.

    You could integrate it with a wiser etc.
     
    znelbok, Aug 1, 2017
    #8
  9. paul.parisi

    daniel C-Busser Moderator

    Joined:
    Jul 26, 2004
    Messages:
    766
    Likes Received:
    20
    Location:
    Adelaide
    Hi Paul,

    I'd say we have the same API spec; the one I got from Airstream has the same title "iZone Ethernet Interface v1.00", is also 18 pages and is dated 27/05/2014. So I think you are right in that the iZone and myzone3 systems share common DNA.

    I have a similar understanding that the iZone system has self-contained logic tailored to work with certain brands of A/C and variable zone motors, completely replacing the manufacturer's logic and on/off motors. I don't want anything more from the API than what the mobile apps can do - certainly don't intend to mess with the core logic. The comment about "reliability in the serial interfacing" is a little worrying, but I guess I will only find out once I start using it.

    If I could go back in time I'd definitely consider Coolmaster. It'd be interesting to see how a Coolmaster solution stacks up price-wise.
     
    daniel, Aug 2, 2017
    #9
  10. paul.parisi

    awynbergen

    Joined:
    Mar 3, 2019
    Messages:
    1
    Likes Received:
    0

    Hi Paul,
    I am a programmer and wishes to develop something for my iZone + iLight system, do you have a contact I can talk to to get access to the API’s, I mean if IFTTT can call my API for lights and AC then why shouldn;’t I be able to do so. I am wanting to development my on api’s to use Apple’s Shortcuts to contriol my system or my try and develop a ‘bridge’ to sit between airstreams api and Apple Homne kit . Can;t understand why Airstream doesn;t gives us Home kit oir even google integration without the IFTTT which fails 99% of the time.

    Unless yu are able to give information on how I can programtically call api on my internal system ?

    Hoping to here from you,

    Regards,.
     
    awynbergen, Mar 3, 2019
    #10
  11. paul.parisi

    daniel C-Busser Moderator

    Joined:
    Jul 26, 2004
    Messages:
    766
    Likes Received:
    20
    Location:
    Adelaide
    Stumbled across this old thread and thought I would post an update.

    I had to find an AC installer that was familiar with Airstream iZone (some will tell you anything to win the job!). I have two iZone controllers which look to be locked down Android touch screens. I put an eDLT alongside each one, the size and style match very nicely.

    There is an iZone master device hidden away that drives the plant (Daikin, but they support at least 7 brands). I have 7 zones each with a variable motor. The direct control logic is self-contained and doesn't let you do anything to hurt the hardware (eg always ensures a spill zone).

    So my AC is independent of C-Bus and has "normal" controllers, ie. good WAF factor.

    The iZone interface's primary purpose is to offer wireless control via their free mobile apps but it also has an Ethernet port. It's tucked away in my SELV cabinet along with switch, NAC, CNI etc.

    I made a script on the NAC to drive the iZone API via Ethernet. It does have a bunch of quirks,the biggest is that you have to send a command several times for it to reliably register in the master, so I just wrapped every call with about 7 loops. Most likely they've done the same in the mobile apps.

    Here is my visualisation which lets me see and control the system wherever I am in the world.

    Screenshot_20191109-075619~2.jpg

    All the zones and functions map to C-Bus groups so the possibilities are endless, eg. room control on gangs. I haven't done that though because there's another script ("Smart AC" button) that drives the whole AC according to weather and solar availability so we rarely even interact with it.

    All in all I'm really happy with the outcome using Airstream iZone. It did take a lot of my time to put together but works well now. Hopefully this is of interest and inspires someone to try a similar integration.
     
    daniel, Nov 8, 2019
    #11
    paul.parisi and Damaxx like this.
  12. paul.parisi

    Narkov

    Joined:
    Nov 12, 2019
    Messages:
    20
    Likes Received:
    4
    Hey Daniel. That's great stuff! Would you be able to post your NAC scripts if possible?
     
    Narkov, Jan 11, 2020
    #12
  13. paul.parisi

    paul.parisi

    Joined:
    Sep 28, 2005
    Messages:
    14
    Likes Received:
    0
    Location:
    Melbourne
    Hi all, years later I finally got around to taking action as the Hitachi system gave up the ghost. To be honest I was most impressed with the latest Advantage Air myair5, they do have a developer forum, however the system is still limited to tiny 10 zones and I couldn't find a single installer that would comfortably do myair5 with an MHI air conditioner system in Victoria so we flipped the coin and went iZone due to the installer options.

    In the end landed with an iZone 435 but unfortunately many problems so far, looks super immature to me and still haven't gotten solutions from iZone team in nearly 6 months on critical issues such as the MHI interface not being able to control the main system correctly. Chase them.. nothing... chase them.. more nothing. I have written up a blog to go into detail as there is almost nothing written on iZone out there.
    https://paulparisi.com.au/wp/blog/2021/12/29/izone-zone-control-experiences-to-date/

    Local distributor is telling me a new CEO is starting and their support issues should improve however so far its been a rough ride and unstable system from day 1.

    That said, I'm working on integrating it via Home Assistant and bridging CBUS capabilities this way, the gap here is reliability as whilst the old Hitachi had hard wired sensors which would give you a guaranteed state of the system (on/off) unfortunately the iZone is not nearly as reliable, as Daniel mentioned the need to loop 7 times to ensure commands get handled, I'm seeing a lot of missed commands, delays in updates, and overriding of commands, plus inaccuracy similar to what others have reported even with a new model iZone system. Some of its so obviously wrong I'm puzzled how iZone's team haven't address some of the issues. This is making integration a real challenge.

    I'd nearly gone down the route of using open source and something like ESPHome for the zone control.. now wishing I'd spend more time on! Fundamentally the zone controller is not complex and not as "smart" as the sales people think.

    Hey Daniel, any chance i could get a copy of your scripts? Those charts look awesome and what I'm wanting to do. I've love to stand up a monitoring dashboard like that. I can feed some of the data back to iZone for debugging as they don't seem to have any diagnostic tools themselves. (yes I am serious, there is no remote firmware update or even local interface and diagnostics is done by sending out someone from the distributor who then removes a part and changing it and hopes it fixes it) Very pre-1980s and only good for simple issues.

    Hey awynbergen, back then I didn't have a contact but now I am pretty familiar with the APIs so happy to help if its still something you're into.
     
    paul.parisi, Jan 4, 2022
    #13
  14. paul.parisi

    daniel C-Busser Moderator

    Joined:
    Jul 26, 2004
    Messages:
    766
    Likes Received:
    20
    Location:
    Adelaide
    Hi Paul, here are my SHAC scripts for the iZone system.

    You'll need to add a user library script, a resident script and an event handler script.

    First the user library (mine is called "user.izone"):

    Code:
    ------------------------------------------------------------
    -- This library offers integration with the
    -- Airstream iZone 325 Climate Control System.
    -- http://izone.com.au
    --
    -- Created by Daniel C
    --   Initial version Feb 2018
    --   Last updated Nov 2020
    --
    -- You will need the following installed & working:
    -- * iZone Controller installed on the wall
    -- * iZone Wifi Bridge on the same subnet as your C-Bus SHAC
    --
    -- Benefits of this library:
    -- * The iZone Wifi Bridge has no authentication measures.  You
    --   should not expose it directly to the internet. This library places
    --   it safely behind your Automation Controller's authentication.
    -- * Remote access via the mobile app requires use of a cloud service
    --   (iZone Worldwide Login)
    -- * The mobile app does not indicate any communication failures,
    --   leaving you thinking your operations worked when they didn't.
    -- * The iZone mobile app UI does not tell you the current temperature.
    -- * The iZone controller & app UIs do not put all the information in one place.
    --
    -- Important notes:
    -- * All C-Bus Groups must first be defined in the SHAC and set to a valid level
    --    otherwise they will not work in the script.
    -- * Sending a POST command once rarely registers with the iZone Bridge.
    --   Sending the same command 5-8 times works a lot better.
    --   The code takes this into account.
    --
    -- TODO:
    -- * Smarter zone queries based on IZONE_ZONES
    -- * Auto-create objects in the script so we aren't dependent on the user creating them.
    ------------------------------------------------------------
    
    ----------------------------------------
    -- SCRIPT CONFIGURATION
    -- You should review and change these as needed
    ----------------------------------------
    
    -- The IP address of the iZone Wifi Bridge
    local IZONE_IP = "192.168.1.19"
    
    -- The number of zones defined in your iZone system
    local IZONE_ZONES = 7
    
    ----------
    -- C-Bus Lighting-like Application
    -- This creates a two-way mapping so that you can control the A/C functions from C-Bus and from visualisations
    -- NOTE: These must be unique and not conflict with any other units or scripts
    -- NOTE: They must also be created on the Objects page and set to an initial value otherwise they will not work!
    
    -- The Application for the following groups (default is 'Heating')
    local CBUS_APP = 136
    
    -- The C-Bus Group to map to the iZone Aircon On/Off state
    local CBUS_SYSTEMON_GROUP = 1
    
    -- The C-Bus Group to map to the iZone Aircon Mode
    local CBUS_MODE_GROUP = 2
    
    -- The C-Bus Group to map to the iZone Aircon Fan state
    local CBUS_FAN_GROUP = 3
    
    -- The C-Bus Groups to map to each of the Zones' On/Off states
    -- Note: the number of these must equal or exceed IZONE_ZONES
    local CBUS_ZONE_GROUPS = { 11, 12, 13, 14, 15, 16, 17, 18 }
    
    ----------
    -- NAC User Param application
    -- This maintains values in realtime so they can be displayed in visualisations and charts
    -- NOTE: These must be unique and not conflict with any other units or scripts
    -- NOTE: They must also be created on the Objects page and set to an initial value otherwise they will not work!
    local CBUS_USERPARAM_APP = 250
    local CBUS_USERPARAM_GA_SETPOINT = "0/250/0"
    local CBUS_USERPARAM_NAME_UPDATED = "acupdated"
    local CBUS_USERPARAM_NAME_SETPOINT = "acsetpoint"
    local CBUS_USERPARAM_NAME_SETPOINTACTIVE = "acsetpointactive"
    local CBUS_USERPARAM_NAME_TEMP = "actemp"
    
    ----------
    -- C-Bus Measurement Application
    -- This produces a report at more leisurely intervals that can be used for charting over longer periods of time
    -- NOTE: These must be unique and not conflict with any other units or scripts
    -- NOTE: They must also be created on the Objects page and set to an initial value otherwise they will not work!
    local CBUS_MEASUREMENT_APP = 228
    local CBUS_MEASUREMENT_DEVICE = 1
    local CBUS_MEASUREMENT_CHANNEL_SETPOINT = 1
    local CBUS_MEASUREMENT_CHANNEL_TEMP = 2
    local CBUS_MEASUREMENT_CHANNEL_SETPOINTACTIVE = 3
    
    -- The time in minutes between each Measurement update
    local CBUS_MEASUREMENT_INTERVAL_MINS = 5
    
    
    ----------------------------------------
    -- SCRIPT CONSTANTS
    -- You should not need to change these
    ----------------------------------------
    
    local CBUS_MEASUREMENT_UNIT_CELSIUS = 0
    
    local CBUS_USERPARAM_NAME_DEBUGLOGGING = "Debug Logging"
    
    -- iZone often ignores a POST command.  It has some timing issue or race condition internally.
    -- To work around this we send a POST several times.  This variable determines the number of times.
    -- 5 attempts seems to succeed about 2/3 of the time.
    -- 8 attempts seems to succeed about 90% of the time.
    local IZONE_POST_ATTEMPTS = 10
    
    -- Keywords used by the iZone API.  These must be in the same order as the corresponding C-Bus Levels, starting from 0, 1, 2, 3...
    local AC_MODE_NAMES = { "cool", "heat", "vent", "dry", "auto" }
    local AC_FAN_NAMES = { "auto", "low", "med", "high" }
    local AC_FAN_NAMES_POST = { "auto", "low", "medium", "high" }
    
    ----------------------------------------
    -- SCRIPT BEGINS
    -- Here be dragons
    ----------------------------------------
    
    -- P is the private package, exposed as 'izone' library
    local P = {}
    izone = P
    
    -- UTILITY FUNCTIONS
    
    local logbuildbuffer = {}
    
    local function logbuild(str)
        logbuildbuffer[#logbuildbuffer+1]=str
    end
    
    local function logflush()
      if ( #logbuildbuffer ~= 0 ) then
        log(table.concat(logbuildbuffer, "\n"))
          logbuildbuffer = {}
      end
    end
    
    local function isDebuggingEnabled()
      return toboolean( GetUserParam(0, CBUS_USERPARAM_NAME_DEBUGLOGGING) )
    end
    
    local function debuglog(str)
      if ( isDebuggingEnabled() ) then
        log(str)
      end
    end
    
    local function debuglogbuild(str)
      if ( isDebuggingEnabled() ) then
        logbuild(str)
      end
    end
    
    local function debuglogflush()
      if ( isDebuggingEnabled() ) then
        logflush()
      end
    end
    
    
    --[[
    SetpointActive object: data = "26"
    pollinterval = ""
    updatetime = "1517922475"
    datatype = "14"
    decoded = "true"
    disablelog = "0"
    tagcache = ""
    id = "4194304003"
    readoninit = "0"
    datahex = "41D00000"
    units = ""
    value = "26"
    highpriolog = "0"
    comment = ""
    address = "0/250/3"
    export = "0"
    ]]--
    
    local function clearObject(name)
      local obj = grp.find(name)
      if ( obj ~= nil ) then
        grp.write(name,0/0)
      end
    end
    
    local function table_print (tt, indent, done)
      done = done or {}
      indent = indent or 0
      if type(tt) == "table" then
        local sb = {}
        for key, value in pairs (tt) do
          table.insert(sb, string.rep (" ", indent)) -- indent it
          if type (value) == "table" and not done [value] then
            done [value] = true
            table.insert(sb, "{\n");
            table.insert(sb, table_print (value, indent + 2, done))
            table.insert(sb, string.rep (" ", indent)) -- indent it
            table.insert(sb, "}\n");
          elseif "number" == type(key) then
            table.insert(sb, string.format("\"%s\"\n", tostring(value)))
          else
            table.insert(sb, string.format(
                "%s = \"%s\"\n", tostring (key), tostring(value)))
           end
        end
        return table.concat(sb)
      else
        return tt .. "\n"
      end
    end
    
    local function tostring2( tbl )
        if  "nil"       == type( tbl ) then
            return tostring(nil)
        elseif  "table" == type( tbl ) then
            return table_print(tbl)
        elseif  "string" == type( tbl ) then
            return tbl
        else
            return tostring(tbl)
        end
    end
    
    -- C-BUS CONVERSION FUNCTIONS
    
    local function table_getIndex(table, value)
      for idx, val in ipairs(table) do
            if val == value then
              return idx
          end
      end
    end
    
    function P.ModeToLevel(acmode)
      local idx = table_getIndex(AC_MODE_NAMES, acmode)
      if ( idx ~= nil ) then
        return idx - 1
      else
        return nil
      end
    end
    
    function P.LevelToMode(cbuslevel)
      return AC_MODE_NAMES[cbuslevel + 1]
    end
    
    function P.FanToLevel(acfan)
      local idx = table_getIndex(AC_FAN_NAMES, acfan)
      if ( idx ~= nil ) then
        return idx - 1
      else
        return nil
      end
    end
    
    function P.LevelToFan(cbuslevel)
      return AC_FAN_NAMES[cbuslevel + 1]
    end
    
    
    
    -- unused function
    -- cbusLevel = (acLevel * 10 ) - 100
    -- AC=10.0 => C-Bus=0, AC=35.5 => C-Bus=255
    function P.TempToLevel(actemp)
      return ( actemp * 10 ) - 100
    end
    
    -- unused function
    -- AcLevel = (cbusLevel / 10 ) + 10
    -- C-Bus=0 => AC=10.0, C-Bus=255 => AC=35.5
    function P.LevelToTemp(cbuslevel)
      return ( cbuslevel / 10 ) + 10
    end
    
    -- C-BUS FUNCTIONS
    
    local function GroupAddressMatches(value,net,app,group)
      return value == net.."/"..app.."/"..group
    end
    
    local function cbus_GetState(app, group)
      return GetCBusState(0, app, group)
    end
    
    local function cbus_GetLevel(app, group)
      return GetCBusLevel(0, app, group)
    end
    
    local function cbus_SetState(app, group, state)
      log("Setting C-Bus Group "..app.."/"..group.." State to "..tostring(state).."...")
      SetCBusState(0, app, group, state)
    end
    
    local function cbus_SetLevel(app, group, level)
      log("Setting C-Bus Group "..app.."/"..group.." Level to "..tostring(level).."...")
      SetCBusLevel(0, app, group, level,0)
    end
    
    function P.GetCbusSystemSettings()
      local result = {}
      result["SystemOn"] = cbus_GetState(CBUS_APP, CBUS_SYSTEMON_GROUP)
      local cbusModeLevel = cbus_GetLevel(CBUS_APP, CBUS_MODE_GROUP)
      result["SystemMode"] = P.LevelToMode(cbusModeLevel)
      result["SystemModeAsLevel"] = cbusModeLevel
      local cbusFanLevel = cbus_GetLevel(CBUS_APP, CBUS_FAN_GROUP)
      result["SystemFan"] = P.LevelToFan(cbusFanLevel)
      result["SystemFanAsLevel"] = cbusFanLevel
      return result
    end
    
    -- iZONE API FUNCTIONS
    
    local http = require("socket.http")
    local json = require("json")
    
    function P.Get(endpoint)
        local result,content,header = http.request('http://' .. IZONE_IP .. '/' .. endpoint )
      return result
    end
    
    function P.GetSystemSettings()
      local result = {}
      local httpresult = P.Get("SystemSettings")
      local httptable = json.decode(httpresult)
      debuglog(httptable)
      result["SystemOn"] = ( httptable["SysOn"] == "on" )
      result["SystemMode"] = ( httptable["SysMode"] )
      result["SystemFan"] = ( httptable["SysFan"] )
      result["Setpoint"] = ( httptable["Setpoint"] )
      result["Temp"] = ( httptable["Temp"] )
      result["ZoneCount"] = ( httptable["NoOfZones"] )
      result["FanAuto"] = ( httptable["FanAuto"] )
      return result
    end
    
    function P.GetZones()
      local result = {}
     
      local httpresult = P.Get("Zones1_4")
      local httptable = json.decode(httpresult)
      debuglog(httptable)
      for httptableindex = 1,4,1
      do
        local resultitem = {}
        resultitem["Number"] = httptableindex
        resultitem["Name"] = httptable[httptableindex]["Name"]
        resultitem["On"] = ( httptable[httptableindex]["Mode"] == "open" )
        table.insert(result, resultitem)
      end
    
      local httpresult = P.Get("Zones5_8")
      local httptable = json.decode(httpresult)
      debuglog(httptable)
      for httptableindex = 1,4,1
      do
        local resultitem = {}
        resultitem["Number"] = httptableindex + 4
        resultitem["Name"] = httptable[httptableindex]["Name"]
        resultitem["On"] = ( httptable[httptableindex]["Mode"] == "open" )
        table.insert(result, resultitem)
      end
    
      -- If you have more than 8 zones add a call to Zones9_12 here
     
      return result
    end
    
    -- Post a command to the aircon
    -- The iZone system seems to require some commands being blasted several times before
    -- it will register.  Tweak the repeatcount until it works.
    function P.Post(endpoint, body, repeatcount)
      local url = 'http://' .. IZONE_IP .. '/' .. endpoint
      log( "POST "..repeatcount.."x :\n"..url.."\n"..body )
      for i = 1,repeatcount,1
      do
            local result,content,header = http.request(url, body)
      end
      return result
    end
    
    -- RESIDENT SCRIPT FUNCTIONS
    -- Create a resident script and add a single line to call the below.
    
    function P.Resident_Poll()
     
      local ac = P.GetSystemSettings()
      ac["SystemModeAsLevel"] = P.ModeToLevel(ac["SystemMode"])
      ac["SystemFanAsLevel"] = P.FanToLevel(ac["SystemFan"])
      debuglog(ac)
    
      -- Update the user parameters as often as we can, for display on screen
      debuglogbuild("Updating user parameters...")
      storage.set("izone.disable_event_handler.setpoint", true)
      SetUserParam(0, CBUS_USERPARAM_NAME_SETPOINT, tostring(ac["Setpoint"]))
      debuglogbuild("Setpoint: "..ac["Setpoint"])
      SetUserParam(0, CBUS_USERPARAM_NAME_TEMP, tostring(ac["Temp"]))
        debuglogbuild("Temp: "..ac["Temp"])
      local nowtext = os.date("%H:%M:%S, %d %b")
      SetUserParam(0, CBUS_USERPARAM_NAME_UPDATED, nowtext)
      debuglogbuild("Updated: "..nowtext)
      -- Update the setpointactive for graphing
      if ( ac["SystemOn"] == true ) then
          SetUserParam(0, CBUS_USERPARAM_NAME_SETPOINTACTIVE, tostring(ac["Setpoint"]))
        debuglogbuild("SetpointActive: "..ac["Setpoint"])
      else
        -- logbuild("SetpointActive: nil")
        -- SetUserParam(0, CBUS_USERPARAM_NAME_SETPOINTACTIVE, nil)
        clearObject("0/250/3")
        debuglogbuild("SetpointActive object: "..tostring2(grp.find("0/250/3")))
      end
      debuglogflush()
    
      -- Send a measurement event at specific intervals, for charting
      local lasttime = storage.get('lastmeasurementtimestamp', 0)
      local thistime = os.time()  -- lua's time resolution is only in seconds
      if ( lasttime > thistime ) then
        lasttime = 0 -- in case the timestamp jumps into the future (it happens sometimes)
      end
      local interval = CBUS_MEASUREMENT_INTERVAL_MINS * 60
      debuglogbuild("Measurement: lasttime=" .. tostring(lasttime) .. " thistime="..tostring(thistime).." interval=" .. tostring(interval) )
      if ( thistime > ( lasttime + interval ) ) then
        debuglogbuild("Sending Measurement events...")
        lasttime = thistime
        SetCBusMeasurement(0, CBUS_MEASUREMENT_DEVICE, CBUS_MEASUREMENT_CHANNEL_SETPOINT, ac["Setpoint"], CBUS_MEASUREMENT_UNIT_CELSIUS)
        SetCBusMeasurement(0, CBUS_MEASUREMENT_DEVICE, CBUS_MEASUREMENT_CHANNEL_TEMP, ac["Temp"], CBUS_MEASUREMENT_UNIT_CELSIUS)
        if ( ac["SystemOn"] == true ) then
            SetCBusMeasurement(0, CBUS_MEASUREMENT_DEVICE, CBUS_MEASUREMENT_CHANNEL_SETPOINTACTIVE, ac["Setpoint"], CBUS_MEASUREMENT_UNIT_CELSIUS)
        else
          debuglogbuild("Setpoint Active : trying to set set to nil so it doesn't plot on trend")
          clearObject("0/"..CBUS_MEASUREMENT_APP.."/"..CBUS_MEASUREMENT_DEVICE.."/"..CBUS_MEASUREMENT_CHANNEL_SETPOINTACTIVE)
        end
        -- set storage variable myobjectdata to a specified value (e.g. 127)
        storage.set('lastmeasurementtimestamp', lasttime)
      end
      debuglogflush()
    
      -- Retrieve the C-Bus groups
    
      debuglogbuild("Updating lighting-like groups...")
      local cbus = P.GetCbusSystemSettings()
      debuglogbuild("System On : ac=" .. tostring(ac["SystemOn"]) .. " cbus=" .. tostring(cbus["SystemOn"]) )
      debuglogbuild("System Mode : ac=" .. tostring(ac["SystemMode"]) .. " ("..tostring(ac["SystemModeAsLevel"])..") cbus=" .. tostring(cbus["SystemMode"]).. " ("..tostring(cbus["SystemModeAsLevel"])..")" )
      local acFanLevel = P.FanToLevel(ac["SystemFan"])
      debuglogbuild("System Fan : ac=" .. tostring(ac["SystemFan"]) .. " ("..tostring(ac["SystemFanAsLevel"])..") cbus=" .. tostring(cbus["SystemFan"]).." (" .. tostring(cbus["SystemFanAsLevel"])..")" )
    
    --[[
        local cbusSetpointLevel = cbus_GetLevel(SYSTEMSETPOINT_APP, SYSTEMSETPOINT_GROUP)
      local cbusSetpointTemp = CbusLevelToAcTemp(cbusSetpointLevel)
      local acSetpointLevel = AcTempToCbusLevel(ac["Setpoint"])
      log("System Setpoint : ac=" .. tostring(ac["Setpoint"]) .. " ("..tostring(acSetpointLevel)..") cbus=" .. tostring(cbusSetpointTemp).. " (".. tostring(cbusSetpointLevel)..")" )
    
      local cbusTempLevel = cbus_GetLevel(SYSTEMTEMP_APP, SYSTEMTEMP_GROUP)
      local cbusTemp = CbusLevelToAcTemp(cbusTempLevel)
      local acTempLevel = AcTempToCbusLevel(ac["Temp"])
      log("System Current Temperature : ac=" .. tostring(ac["Temp"]) .. " ("..tostring(acTempLevel)..") cbus=" .. tostring(cbusTemp).. " (".. tostring(cbusTempLevel)..")" )
    ]]
    
      local acz = P.GetZones()
      debuglog(acz)
     
      local acZoneCount = ac["ZoneCount"]
      for zoneIndex = 1,acZoneCount,1
      do
        local cbusZoneOn = cbus_GetState(CBUS_APP, CBUS_ZONE_GROUPS[zoneIndex])
        local acZoneOn = acz[zoneIndex]["On"]
        debuglogbuild("Zone " .. zoneIndex .. " On : ac=" .. tostring(acZoneOn) .. " cbus=" .. tostring(cbusZoneOn))
      end
     
      logflush()
     
      -- Update the C-Bus groups, if needed
     
      if ( ac["SystemOn"] ~= cbus["SystemOn"] ) then
        storage.set("izone.disable_event_handler.systemon", true)
        cbus_SetState(CBUS_APP, CBUS_SYSTEMON_GROUP, ac["SystemOn"])
      end
    
      if ( ac["SystemMode"] ~= cbus["SystemMode"] ) then
        storage.set("izone.disable_event_handler.systemmode", true)
        cbus_SetLevel(CBUS_APP, CBUS_MODE_GROUP, ac["SystemModeAsLevel"])
      end
    
      if ( ac["SystemFanAsLevel"] ~= nil and ac["SystemFanAsLevel"] ~= cbus["SystemFanAsLevel"] ) then
        storage.set("izone.disable_event_handler.systemfan", true)
        cbus_SetLevel(CBUS_APP, CBUS_FAN_GROUP, ac["SystemFanAsLevel"])
      end
    
    --[[
      if ( acSetpointLevel ~= nil and acSetpointLevel ~= cbusSetpointLevel ) then
        cbus_SetLevel(SYSTEMSETPOINT_APP, SYSTEMSETPOINT_GROUP, acSetpointLevel)
      end
     
      if ( acTempLevel ~= nil and acTempLevel ~= cbusTempLevel ) then
        cbus_SetLevel(SYSTEMTEMP_APP, SYSTEMTEMP_GROUP, acTempLevel)
      end
    ]]
     
      for zoneIndex = 1,acZoneCount,1
      do
        local cbusZoneOn = cbus_GetState(CBUS_APP, CBUS_ZONE_GROUPS[zoneIndex])
        local acZoneOn = acz[zoneIndex]["On"]
        if ( acZoneOn ~= cbusZoneOn ) then
          storage.set("izone.disable_event_handler.systemzones", true)
          cbus_SetState(CBUS_APP, CBUS_ZONE_GROUPS[zoneIndex], acZoneOn)
        end       
      end
     
    end
    
    
    -- Global Event Handler
    -- Create an event script that contains a single line to call this function.
    -- Associate it with a keyword such as "izone_event" and add this keyword to all the relevant groups:
    --   CBUS_SYSTEMON_GROUP
    --   CBUS_MODE_GROUP
    --   CBUS_FAN_GROUP
    --   CBUS_ZONE_GROUPS[]
    --   CBUS_USERPARAM_NAME_SETPOINT
    
    function P.Event_Handler()
    
      -- event.sender is either "us" (sent over bus) or "sr" (sent from within the script)
      if ( event.sender ~= "us" ) then
        -- if we triggered this from within user.izone then don't process it as an intentional change
        if ( storage.get("izone.disable_event_handler", false) == true ) then
          return
        end
      end
     
    -- log( tostring2( event) )
    
    --  local cbus = P.GetCbusSystemSettings()
     
      if ( GroupAddressMatches( event.dst, 0, CBUS_APP, CBUS_SYSTEMON_GROUP ) ) then
        if ( storage.get("izone.disable_event_handler.systemon") == true ) then
          storage.delete("izone.disable_event_handler.systemon")
          return
        else
          local cbusSystemOn = GetCBusState(0, CBUS_APP, CBUS_SYSTEMON_GROUP)
          if ( cbusSystemOn == true ) then
            log("Executing action: Airconditioner on")
            P.Post("SystemON", '{"SystemON":"on"}', IZONE_POST_ATTEMPTS)
          else
            log("Executing action: Airconditioner off")
            P.Post("SystemON", '{"SystemON":"off"}', IZONE_POST_ATTEMPTS)
          end
        end
      elseif ( GroupAddressMatches( event.dst, 0, CBUS_APP, CBUS_MODE_GROUP ) ) then
        if ( storage.get("izone.disable_event_handler.systemmode") == true ) then
          storage.delete("izone.disable_event_handler.systemmode")
          return
        else
          local cbusModeLevel = GetCBusLevel(0, CBUS_APP, CBUS_MODE_GROUP)
          local cbusMode = P.LevelToMode(cbusModeLevel)
          log("Executing action: Mode '" .. tostring(cbusMode) .. "'.")
          P.Post("SystemMODE", '{"SystemMODE":"' .. cbusMode .. '"}', IZONE_POST_ATTEMPTS)
        end
      elseif ( GroupAddressMatches( event.dst, 0, CBUS_APP, CBUS_FAN_GROUP ) ) then
        if ( storage.get("izone.disable_event_handler.systemfan") == true ) then
          storage.delete("izone.disable_event_handler.systemfan")
          return
        else
          local cbusFanLevel = GetCBusLevel(0, CBUS_APP, CBUS_FAN_GROUP)
          local cbusFan = AC_FAN_NAMES_POST[cbusFanLevel + 1]
          log("Executing action: Fan '" .. tostring(cbusFan) .. "'.")
          P.Post("SystemFAN", '{"SystemFAN":"' .. cbusFan .. '"}', IZONE_POST_ATTEMPTS)
        end
      elseif ( event.dst == CBUS_USERPARAM_GA_SETPOINT ) then
        if ( storage.get("izone.disable_event_handler.setpoint") == true ) then
          storage.delete("izone.disable_event_handler.setpoint")
          return
        else
          local cbusSetpoint = GetUserParam(0, CBUS_USERPARAM_NAME_SETPOINT)
          log("Executing action: Setpoint '" .. tostring(cbusSetpoint) .. "'.")
          P.Post("UnitSetpoint", '{"UnitSetpoint":"' .. cbusSetpoint .. '"}', IZONE_POST_ATTEMPTS)
        end
      else
          for zoneIndex = 1,IZONE_ZONES,1
        do
          if ( GroupAddressMatches( event.dst, 0, CBUS_APP, CBUS_ZONE_GROUPS[zoneIndex] ) ) then
            if ( storage.get("izone.disable_event_handler.systemzones") == true ) then
              storage.delete("izone.disable_event_handler.systemzones")
              return
            else
              local cbusZoneOn = GetCBusState(0, CBUS_APP, CBUS_ZONE_GROUPS[zoneIndex])
              if ( cbusZoneOn == true ) then
                log("Executing action: Zone " .. zoneIndex .. " on.")
                local body = '{"ZoneCommand":{"ZoneNo":"' .. zoneIndex .. '","Command":"open"}}'
                P.Post("ZoneCommand", body, IZONE_POST_ATTEMPTS)
              else
                log("Executing action: Zone " .. zoneIndex .. " off.")
                local body = '{"ZoneCommand":{"ZoneNo":"' .. zoneIndex .. '","Command":"close"}}'
                P.Post("ZoneCommand", body, IZONE_POST_ATTEMPTS)
              end
            end
            do return end   
          end
        end
     
          log("Could not determine action.")
      end
    end
     
    -- return the library
    return P
    

    The resident script is simple enough - I have it set to execute every 5 seconds.

    Code:
    require("user.izone")
    izone.Resident_Poll()
    
    Likewise the event handler script - when you create it specify a keyword such as "izone_event".

    Code:
    require("user.izone")
    izone.Event_Handler()
    
    Finally, in the SHAC create the C-Bus Groups needed by the script and give them the same keyword; this allows the script to trigger instantly when you change the levels of those groups in C-Bus. This is what gives you true bi-directional sync+control, where you can use the iZone controller display or a SHAC visualization, and they'll each update to reflect any change made in the other.

    upload_2022-3-21_21-24-37.png

    I hope you can use these. Make sure to read the comments and update the script as needed to suit your system. I only wrote what I needed, e.g. retrieved up to 8 zones, if you have more you'd need to add the extra commands where indicated.

    As you might guess, the script has been remarkably stable over the years. It works well enough that I've not had any reason to tinker with it.

    Good luck!

    EDIT: they're now on Github here: https://github.com/dannycoil/nac-izone
     
    Last edited: Mar 23, 2022
    daniel, Mar 21, 2022
    #14
    Damaxx likes this.
  15. paul.parisi

    Ks04

    Joined:
    Dec 28, 2019
    Messages:
    107
    Likes Received:
    9
    Hey There,

    I've been using this script to get updates from the iZone controller which has been working perfectly, however I'm having issues sending commands to the API.

    I've tried using postman and what appears to be identical commends works perfect every time, but I cannot get the API to respond even with sending 20 times.

    I added some extra logging and it appears the body is correct, the URL correct and I'm getting 200 back (though this API seems to send that back no matter what you send...).

    Anyone else had any issues with this? I don't understand why it would work with postman, but not the SHAC...

    upload_2023-1-27_12-41-51.png
     
    Ks04, Jan 27, 2023
    #15
  16. paul.parisi

    Ks04

    Joined:
    Dec 28, 2019
    Messages:
    107
    Likes Received:
    9
    Some additional diagnostics - I tried sending the body {"StatusRequest":"System"} to get the status via POST but also receive a blank response (200) via the SHAC. I also tried a post request for a different system and was able to get everything back as expected.

    One thought is that the SHAC is doing something funny to the formatting, but that wouldn't explain why it works for some here... what firmware version are people running? And/or any other thoughts?
     
    Ks04, Jan 27, 2023
    #16
  17. paul.parisi

    Ks04

    Joined:
    Dec 28, 2019
    Messages:
    107
    Likes Received:
    9
    To confuse things even more, I've been testing this further... using the postman echo server, I can see both the same body being sent by both postman and the SHAC (postman works, SHAC does not). I then installed LUA on my laptop and ran the same script, and it worked without issues.

    upload_2023-1-29_11-0-13.png

    Is the script working for others?
     
    Ks04, Jan 29, 2023
    #17
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.