<?xml version="1.0" encoding="utf-8"?>
<Templates format="2" protocolVersion="83">
  <Template id="a3f1c2d4-e5b6-47f8-9a0b-1c2d3e4f5a6b" revision="1.0">
    <Name>Vaillant aroTHERM plus + sensoCOMFORT VRC 720 (micro-ebusd HTTP)</Name>
    <CcuModel>PacketParserCCU</CcuModel>
    <Producer>Vaillant</Producer>
    <Model>aroTHERM plus + sensoCOMFORT VRC 720</Model>
    <Description>Vaillant aroTHERM plus heat pump and sensoCOMFORT VRC 720 controller via eBUS Adapter Shield/Stick C6 running micro-ebusd HTTP JSON API.</Description>
    <Category>1</Category>
    <ImportParameters>
      <Parameter>
        <Id>ADAPTER_IP</Id>
        <Name>eBUS Adapter IP Address</Name>
        <Value/>
      </Parameter>
      <Parameter>
        <Id>CONTROLLER_CIRCUIT</Id>
        <Name>Controller circuit name (e.g. ctlv2 for VRC 720, 700 for VRC 700)</Name>
        <Value>ctlv2</Value>
      </Parameter>
      <Parameter>
        <Id>HEAT_SOURCE_CIRCUIT</Id>
        <Name>Heat source circuit name (e.g. hmu for aroTHERM plus)</Name>
        <Value>hmu</Value>
      </Parameter>
    </ImportParameters>
    <Module>
      <Name>sensoCOMFORT VRC 720 (ctlv2)</Name>
      <Id>-1</Id>
      <Model>PacketParserModuleHttp</Model>
      <DeviceProperties>
        <IpAddress>$[ADAPTER_IP]</IpAddress>
        <Port>80</Port>
        <UseHttps>False</UseHttps>
        <HttpAuthentification>0</HttpAuthentification>
        <InternalPollInterval>60000</InternalPollInterval>
        <ReadScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/Currenterror/get?from=1&quot;);
IF response.IsSuccess
  VAR e0 := PARSEJSON(response.Content, &quot;data.fields.error&quot;, true);
  VAR e1 := PARSEJSON(response.Content, &quot;data.fields.error_1&quot;, true);
  VAR e2 := PARSEJSON(response.Content, &quot;data.fields.error_2&quot;, true);
  VAR e3 := PARSEJSON(response.Content, &quot;data.fields.error_3&quot;, true);
  VAR e4 := PARSEJSON(response.Content, &quot;data.fields.error_4&quot;, true);
  IF !ISNULL(e0)
    IF e0 != 0
      ADDERROR(0, &quot;Controller error code: &quot; + e0);
    END
  END
  IF !ISNULL(e1)
    IF e1 != 0
      ADDERROR(1, &quot;Controller error_1 code: &quot; + e1);
    END
  END
  IF !ISNULL(e2)
    IF e2 != 0
      ADDERROR(2, &quot;Controller error_2 code: &quot; + e2);
    END
  END
  IF !ISNULL(e3)
    IF e3 != 0
      ADDERROR(3, &quot;Controller error_3 code: &quot; + e3);
    END
  END
  IF !ISNULL(e4)
    IF e4 != 0
      ADDERROR(4, &quot;Controller error_4 code: &quot; + e4);
    END
  END
END</ReadScriptPacketParser>
      </DeviceProperties>
      <Devices>
        <Device>
          <Name>Outdoor Temperature</Name>
          <Id>-2</Id>
          <Model>PacketParserTemperatureSensor</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <ReadTemperatureScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/DisplayedOutsideTemp/get?from=1&quot;);
IF response.IsSuccess
  RETURN(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
END
RETURN(NaN);</ReadTemperatureScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Zone 1 Thermostat</Name>
          <Id>-3</Id>
          <Model>PacketParserThermostat</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <MinTemperature>5</MinTemperature>
            <MaxTemperature>30</MaxTemperature>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <StartupValueSource>ReadFromDevice</StartupValueSource>
            <ReadCurrentTemperatureScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/Z1RoomTemp/get?from=1&quot;);
IF response.IsSuccess
  RETURN(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
END
RETURN(NaN);</ReadCurrentTemperatureScriptPacketParser>
            <ReadTemperatureSetPointScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/Z1ActualRoomTempDesired/get?from=1&quot;);
IF response.IsSuccess
  RETURN(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
END
RETURN(NaN);</ReadTemperatureSetPointScriptPacketParser>
            <WriteTemperatureSetPointScriptPacketParser>VAR body := &quot;{\&quot;fields\&quot;:{\&quot;value\&quot;:&quot; + Se + &quot;}}&quot;;
SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/Z1QuickVetoTemp/set&quot;, &quot;PUT&quot;, body, &quot;Content-Type: application/json&quot;);
SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/Z1QuickVetoTemp/get?from=2&quot;);</WriteTemperatureSetPointScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Zone 1 Operating Mode</Name>
          <Id>-4</Id>
          <Model>PacketParserMultiValueSwitch</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <StartupValueSource>ReadFromDevice</StartupValueSource>
            <OffStateValue>0</OffStateValue>
            <ReadSwitchStateScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/Z1OpMode/get?from=1&quot;);
IF response.IsSuccess
  # micro-ebusd returns the name of the value from the ebusd definition (off, auto, day, night); a number is accepted too
  VAR v := TOSTRING(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
  IF v = &quot;off&quot; OR v = &quot;0&quot;
    RETURN(0);
  END
  IF v = &quot;auto&quot; OR v = &quot;1&quot;
    RETURN(1);
  END
  IF v = &quot;day&quot; OR v = &quot;manual&quot; OR v = &quot;2&quot;
    RETURN(2);
  END
  IF v = &quot;night&quot; OR v = &quot;3&quot;
    RETURN(3);
  END
END
RETURN(NaN);</ReadSwitchStateScriptPacketParser>
            <WriteSwitchStateScriptPacketParser># Names from the ebusd definition; on the VRC 720 &quot;day&quot; is the manual mode
VAR name := SWITCH(Mu, 0, &quot;off&quot;, 1, &quot;auto&quot;, 2, &quot;day&quot;, 3, &quot;night&quot;, &quot;&quot;);
IF name != &quot;&quot;
  SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/Z1OpMode/set&quot;, &quot;PUT&quot;, &quot;{\&quot;fields\&quot;:{\&quot;value\&quot;:\&quot;&quot; + name + &quot;\&quot;}}&quot;, &quot;Content-Type: application/json&quot;);
  # Read back from the bus: a write the controller ignored shows the real mode at the next read
  SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/Z1OpMode/get?from=2&quot;);
END</WriteSwitchStateScriptPacketParser>
            <Value0>0</Value0>
            <Value0Index>0</Value0Index>
            <Value0Name>Off</Value0Name>
            <Value0IconId>4</Value0IconId>
            <Value1>1</Value1>
            <Value1Index>1</Value1Index>
            <Value1Name>Auto</Value1Name>
            <Value1IconId>3</Value1IconId>
            <Value2>2</Value2>
            <Value2Index>2</Value2Index>
            <Value2Name>Manual / Day</Value2Name>
            <Value2IconId>1</Value2IconId>
            <Value3>3</Value3>
            <Value3Index>3</Value3Index>
            <Value3Name>Night</Value3Name>
            <Value3IconId>47</Value3IconId>
            <Value4Index>68</Value4Index>
            <Value5Index>69</Value5Index>
            <Value6Index>70</Value6Index>
            <Value7Index>71</Value7Index>
            <Value8Index>72</Value8Index>
            <Value9Index>73</Value9Index>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Hot Water Thermostat</Name>
          <Id>-5</Id>
          <Model>PacketParserThermostat</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <MinTemperature>35</MinTemperature>
            <MaxTemperature>75</MaxTemperature>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <StartupValueSource>ReadFromDevice</StartupValueSource>
            <ReadCurrentTemperatureScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcStorageTemp/get?from=1&quot;);
IF response.IsSuccess
  RETURN(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
END
RETURN(NaN);</ReadCurrentTemperatureScriptPacketParser>
            <ReadTemperatureSetPointScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcTempDesired/get?from=1&quot;);
IF response.IsSuccess
  RETURN(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
END
RETURN(NaN);</ReadTemperatureSetPointScriptPacketParser>
            <WriteTemperatureSetPointScriptPacketParser>VAR body := &quot;{\&quot;fields\&quot;:{\&quot;value\&quot;:&quot; + Se + &quot;}}&quot;;
SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcTempDesired/set&quot;, &quot;PUT&quot;, body, &quot;Content-Type: application/json&quot;);
SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcTempDesired/get?from=2&quot;);</WriteTemperatureSetPointScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Hot Water Operating Mode</Name>
          <Id>-6</Id>
          <Model>PacketParserMultiValueSwitch</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <StartupValueSource>ReadFromDevice</StartupValueSource>
            <OffStateValue>0</OffStateValue>
            <ReadSwitchStateScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcOpMode/get?from=1&quot;);
IF response.IsSuccess
  # micro-ebusd returns the name of the value from the ebusd definition (off, auto, day, night); a number is accepted too
  VAR v := TOSTRING(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
  IF v = &quot;off&quot; OR v = &quot;0&quot;
    RETURN(0);
  END
  IF v = &quot;auto&quot; OR v = &quot;1&quot;
    RETURN(1);
  END
  IF v = &quot;day&quot; OR v = &quot;manual&quot; OR v = &quot;2&quot;
    RETURN(2);
  END
  IF v = &quot;night&quot; OR v = &quot;3&quot;
    RETURN(3);
  END
END
RETURN(NaN);</ReadSwitchStateScriptPacketParser>
            <WriteSwitchStateScriptPacketParser># Names from the ebusd definition; on the VRC 720 &quot;day&quot; is the manual mode
VAR name := SWITCH(Mu, 0, &quot;off&quot;, 1, &quot;auto&quot;, 2, &quot;day&quot;, 3, &quot;night&quot;, &quot;&quot;);
IF name != &quot;&quot;
  SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcOpMode/set&quot;, &quot;PUT&quot;, &quot;{\&quot;fields\&quot;:{\&quot;value\&quot;:\&quot;&quot; + name + &quot;\&quot;}}&quot;, &quot;Content-Type: application/json&quot;);
  # Read back from the bus: a write the controller ignored shows the real mode at the next read
  SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcOpMode/get?from=2&quot;);
END</WriteSwitchStateScriptPacketParser>
            <Value0>0</Value0>
            <Value0Index>0</Value0Index>
            <Value0Name>Off</Value0Name>
            <Value0IconId>4</Value0IconId>
            <Value1>1</Value1>
            <Value1Index>1</Value1Index>
            <Value1Name>Auto</Value1Name>
            <Value1IconId>3</Value1IconId>
            <Value2>2</Value2>
            <Value2Index>2</Value2Index>
            <Value2Name>Day</Value2Name>
            <Value2IconId>1</Value2IconId>
            <Value3>3</Value3>
            <Value3Index>3</Value3Index>
            <Value3Name>Night</Value3Name>
            <Value3IconId>47</Value3IconId>
            <Value4Index>68</Value4Index>
            <Value5Index>69</Value5Index>
            <Value6Index>70</Value6Index>
            <Value7Index>71</Value7Index>
            <Value8Index>72</Value8Index>
            <Value9Index>73</Value9Index>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Hot Water Boost</Name>
          <Id>-7</Id>
          <Model>PacketParserSwitch</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <OnStateIconId>82</OnStateIconId>
            <OffStateIconId>83</OffStateIconId>
            <OnStateName>Active</OnStateName>
            <OffStateName>Off</OffStateName>
            <StartupValueSource>ReadFromDevice</StartupValueSource>
            <ReadSwitchStateScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcSFMode/get?from=1&quot;);
IF response.IsSuccess
  VAR v := TOSTRING(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
  IF v = &quot;load&quot; OR v = &quot;6&quot;
    RETURN(1);
  END
  RETURN(0);
END
RETURN(NaN);</ReadSwitchStateScriptPacketParser>
            <WriteSwitchStateScriptPacketParser># load = one-time hot water charge; the controller returns to auto by itself when the tank is charged
VAR name := IF(St, &quot;load&quot;, &quot;auto&quot;);
SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcSFMode/set&quot;, &quot;PUT&quot;, &quot;{\&quot;fields\&quot;:{\&quot;value\&quot;:\&quot;&quot; + name + &quot;\&quot;}}&quot;, &quot;Content-Type: application/json&quot;);
SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/HwcSFMode/get?from=2&quot;);</WriteSwitchStateScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Flow Temperature</Name>
          <Id>-8</Id>
          <Model>PacketParserTemperatureSensor</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <ReadTemperatureScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/SystemFlowTemp/get?from=1&quot;);
IF response.IsSuccess
  RETURN(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
END
RETURN(NaN);</ReadTemperatureScriptPacketParser>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Water Pressure</Name>
          <Id>-9</Id>
          <Model>PacketParserVariable</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <IsReadOnly>True</IsReadOnly>
            <ReadStateScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[CONTROLLER_CIRCUIT]&quot; + &quot;/WaterPressure/get?from=1&quot;);
IF response.IsSuccess
  RETURN(PARSEJSON(response.Content, &quot;data.fields.value&quot;, true));
END
RETURN(NaN);</ReadStateScriptPacketParser>
          </DeviceProperties>
        </Device>
      </Devices>
    </Module>
    <Module>
      <Name>aroTHERM plus (hmu)</Name>
      <Id>-11</Id>
      <Model>PacketParserModuleHttp</Model>
      <DeviceProperties>
        <IpAddress>$[ADAPTER_IP]</IpAddress>
        <Port>80</Port>
        <UseHttps>False</UseHttps>
        <HttpAuthentification>0</HttpAuthentification>
        <InternalPollInterval>60000</InternalPollInterval>
      </DeviceProperties>
      <Devices>
        <Device>
          <Name>Heat Pump Electricity</Name>
          <Id>-12</Id>
          <Model>PacketParserElectricityMeter</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <Calibration>0</Calibration>
            <ReadScriptPacketParser>VAR response := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[HEAT_SOURCE_CIRCUIT]&quot; + &quot;/CurrentConsumedPower/get?from=1&quot;);
IF response.IsSuccess
  HmuPower := response.Content;
END
VAR rEnergy := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[HEAT_SOURCE_CIRCUIT]&quot; + &quot;/TotalEnergyUsage/get?from=1&quot;);
IF rEnergy.IsSuccess
  HmuEnergy := rEnergy.Content;
END
VAR rCop := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[HEAT_SOURCE_CIRCUIT]&quot; + &quot;/CopHc/get?from=1&quot;);
IF rCop.IsSuccess
  HmuCop := rCop.Content;
END
VAR rYield := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[HEAT_SOURCE_CIRCUIT]&quot; + &quot;/YieldTotal/get?from=1&quot;);
IF rYield.IsSuccess
  HmuYield := rYield.Content;
END
VAR rState := SENDHTTPREQUEST(&quot;/api/v1/ebus/&quot; + &quot;$[HEAT_SOURCE_CIRCUIT]&quot; + &quot;/State/get?from=1&quot;);
IF rState.IsSuccess
  HmuState := rState.Content;
END</ReadScriptPacketParser>
            <CustomVariables>[{&quot;Name&quot;:&quot;HmuPower&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0,&quot;StringValue&quot;:&quot;&quot;},{&quot;Name&quot;:&quot;HmuEnergy&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0,&quot;StringValue&quot;:&quot;&quot;},{&quot;Name&quot;:&quot;HmuCop&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0,&quot;StringValue&quot;:&quot;&quot;},{&quot;Name&quot;:&quot;HmuYield&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0,&quot;StringValue&quot;:&quot;&quot;},{&quot;Name&quot;:&quot;HmuState&quot;,&quot;ValueTypeId&quot;:2,&quot;NumericValue&quot;:0,&quot;StringValue&quot;:&quot;&quot;}]</CustomVariables>
            <ReadDemandScriptPacketParser>RETURN(PARSEJSON(HmuPower, &quot;data.fields.value&quot;, true));</ReadDemandScriptPacketParser>
            <ReadTotalConsumptionScriptPacketParser>RETURN(PARSEJSON(HmuEnergy, &quot;data.fields.value&quot;, true));</ReadTotalConsumptionScriptPacketParser>
            <ServiceAttributesScriptsPacketParser>[{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;COP (heating)&quot;},&quot;ReadFormula&quot;:&quot;RETURN(PARSEJSON(HmuCop, \&quot;data.fields.value\&quot;, true));&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Total thermal yield [kWh]&quot;},&quot;ReadFormula&quot;:&quot;RETURN(PARSEJSON(HmuYield, \&quot;data.fields.value\&quot;, true));&quot;},{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Operational state&quot;},&quot;ReadFormula&quot;:&quot;VAR s := PARSEJSON(HmuState, \&quot;data.fields.state\&quot;, true);\nIF ISNULL(s)\n  RETURN(\&quot;-\&quot;);\nEND\nRETURN(TOSTRING(s));&quot;}]</ServiceAttributesScriptsPacketParser>
          </DeviceProperties>
        </Device>
      </Devices>
    </Module>
  </Template>
</Templates>
