<?xml version="1.0" encoding="utf-8"?>
<Templates format="2" protocolVersion="81">
  <Template id="c3e1a7d2-58b4-4f0e-b2a9-7d16e4f80b35" revision="1.0">
    <SuggestedCCUParameters>
      <Baudrate>19200</Baudrate>
      <Parity>None</Parity>
      <StopBits>One</StopBits>
      <DataBits>8</DataBits>
      <UseAsciiCommunication>False</UseAsciiCommunication>
      <DelayBetweenRequests>-1</DelayBetweenRequests>
      <ReadWriteTimeout>1000</ReadWriteTimeout>
    </SuggestedCCUParameters>
    <RevisionHistory>
      <Revision id="1.0">Community version 2026-10-14: corrected readings of the official Gen4 template</Revision>
    </RevisionHistory>
    <Name>SolaX X1/X3-Hybrid G4 — corrected monitoring (Modbus RTU)</Name>
    <CcuModel>ModbusRtuCCU</CcuModel>
    <Producer>SolaX Power</Producer>
    <Model>X3-Hybrid G4 (X1 partially)</Model>
    <Description>Official SolaX Gen4 template with corrected readings: fault code word order, total energy scale, PV production meter, signed battery temperature. Writes stay disabled.</Description>
    <Category>2</Category>
    <ImportParameters>
      <Parameter>
        <Id>SlaveId</Id>
        <Name>Modbus Slave ID</Name>
        <Value>1</Value>
      </Parameter>
    </ImportParameters>
    <Module>
      <Name>SolaX X3-Hybrid G4</Name>
      <Id>-1</Id>
      <Model>ModbusModule</Model>
      <DeviceProperties>
        <DeviceType>7001</DeviceType>
        <InternalPollInterval>30000</InternalPollInterval>
        <SlaveId>$[SlaveId]</SlaveId>
        <MaxPrefetchGroupSize>32</MaxPrefetchGroupSize>
        <PrefetchModeId>0</PrefetchModeId>
        <ReadScript>VAR mode := MODBUSR(A, 0x0009, UInt16);
IF(mode = 3, ADDERROR(1, &quot;Inverter fault (run mode 3)&quot;));
IF(mode = 4, ADDERROR(2, &quot;Inverter permanent fault (run mode 4)&quot;));
VAR inv := MODBUSR(A, 0x0040, LittleEndianUint32);
IF(GETBIT(inv, 0) = 1, ADDERROR(100, &quot;TZ Protect Fault&quot;));
IF(GETBIT(inv, 1) = 1, ADDERROR(101, &quot;Grid Lost Fault&quot;));
IF(GETBIT(inv, 2) = 1, ADDERROR(102, &quot;Grid Volt Fault&quot;));
IF(GETBIT(inv, 3) = 1, ADDERROR(103, &quot;Grid Freq Fault&quot;));
IF(GETBIT(inv, 4) = 1, ADDERROR(104, &quot;PV Volt Fault&quot;));
IF(GETBIT(inv, 5) = 1, ADDERROR(105, &quot;Bus Volt Fault&quot;));
IF(GETBIT(inv, 6) = 1, ADDERROR(106, &quot;Bat Volt Fault&quot;));
IF(GETBIT(inv, 7) = 1, ADDERROR(107, &quot;AC10mins Volt Fault&quot;));
IF(GETBIT(inv, 8) = 1, ADDERROR(108, &quot;DCI OCP Fault&quot;));
IF(GETBIT(inv, 9) = 1, ADDERROR(109, &quot;DCV OCP Fault&quot;));
IF(GETBIT(inv, 10) = 1, ADDERROR(110, &quot;SW OCP Fault&quot;));
IF(GETBIT(inv, 11) = 1, ADDERROR(111, &quot;RC OCP Fault&quot;));
IF(GETBIT(inv, 12) = 1, ADDERROR(112, &quot;Isolation Fault&quot;));
IF(GETBIT(inv, 13) = 1, ADDERROR(113, &quot;Temp Over Fault&quot;));
IF(GETBIT(inv, 14) = 1, ADDERROR(114, &quot;BatConnDir Fault&quot;));
IF(GETBIT(inv, 15) = 1, ADDERROR(115, &quot;Off-grid Overload&quot;));
IF(GETBIT(inv, 16) = 1, ADDERROR(116, &quot;Overload&quot;));
IF(GETBIT(inv, 17) = 1, ADDERROR(117, &quot;Bat Power Low&quot;));
IF(GETBIT(inv, 18) = 1, ADDERROR(118, &quot;BMS Lost&quot;));
IF(GETBIT(inv, 19) = 1, ADDERROR(119, &quot;Fan Fault&quot;));
IF(GETBIT(inv, 20) = 1, ADDERROR(120, &quot;Low Temp Fault&quot;));
IF(GETBIT(inv, 23) = 1, ADDERROR(123, &quot;INV Volt Sample Fault&quot;));
IF(GETBIT(inv, 24) = 1, ADDERROR(124, &quot;Inner Comm Fault&quot;));
IF(GETBIT(inv, 25) = 1, ADDERROR(125, &quot;INV EEPROM Fault&quot;));
IF(GETBIT(inv, 26) = 1, ADDERROR(126, &quot;RCD Fault&quot;));
IF(GETBIT(inv, 27) = 1, ADDERROR(127, &quot;Grid Relay Fault&quot;));
IF(GETBIT(inv, 28) = 1, ADDERROR(128, &quot;Off-grid Relay Fault&quot;));
IF(GETBIT(inv, 29) = 1, ADDERROR(129, &quot;PV ConnDir Fault&quot;));
IF(GETBIT(inv, 30) = 1, ADDERROR(130, &quot;Charger Relay Fault&quot;));
IF(GETBIT(inv, 31) = 1, ADDERROR(131, &quot;Earth Relay Fault&quot;));
VAR mgr := MODBUSR(A, 0x0043, UInt16);
IF(GETBIT(mgr, 0) = 1, ADDERROR(200, &quot;Power Type Fault&quot;));
IF(GETBIT(mgr, 1) = 1, ADDWARNING(201, &quot;Port OC Warning&quot;));
IF(GETBIT(mgr, 2) = 1, ADDERROR(202, &quot;Mgr EEPROM Fault&quot;));
IF(GETBIT(mgr, 4) = 1, ADDERROR(204, &quot;NTC Sample Invalid&quot;));
IF(GETBIT(mgr, 5) = 1, ADDERROR(205, &quot;Battery Temperature Low&quot;));
IF(GETBIT(mgr, 6) = 1, ADDERROR(206, &quot;Battery Temperature High&quot;));
IF(GETBIT(mgr, 9) = 1, ADDERROR(209, &quot;Meter Fault&quot;));
IF(GETBIT(mgr, 10) = 1, ADDERROR(210, &quot;Bypass Relay Fault&quot;));
IF(GETBIT(mgr, 11) = 1, ADDERROR(211, &quot;Fan 2 Fault&quot;));</ReadScript>
        <ServiceAttributesFormulas>[{&quot;AttributeDefinition&quot;:{&quot;Name&quot;:&quot;Run mode&quot;},&quot;ReadFormula&quot;:&quot;SWITCH(MODBUSR(A, 0x0009, UInt16), 0, \&quot;Waiting\&quot;, 1, \&quot;Checking\&quot;, 2, \&quot;Normal\&quot;, 3, \&quot;Fault\&quot;, 4, \&quot;Permanent fault\&quot;, 5, \&quot;Update\&quot;, 6, \&quot;Off-grid waiting\&quot;, 7, \&quot;Off-grid\&quot;, 8, \&quot;Self testing\&quot;, 9, \&quot;Idle\&quot;, 10, \&quot;Standby\&quot;, \&quot;Other\&quot;)&quot;}]</ServiceAttributesFormulas>
      </DeviceProperties>
      <Devices>
        <Device>
          <Name>${deviceType_BatterySOC}</Name>
          <Id>-2</Id>
          <Model>ModbusAnalogInput</Model>
          <DeviceProperties>
            <DeviceType>11001</DeviceType>
            <InternalPollInterval>15000</InternalPollInterval>
            <ReadInputLevel>MODBUSR(A, 0x001C, UInt16) / 100</ReadInputLevel>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Battery Temperature</Name>
          <Id>-3</Id>
          <Model>ModbusTemperatureSensor</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>80000</InternalPollInterval>
            <ReadTemperature>MODBUSR(A, 0x0018, Int16)</ReadTemperature>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Daily/Actual Energy</Name>
          <Id>-4</Id>
          <Model>ModbusElectricityMeter</Model>
          <DeviceProperties>
            <DeviceType>9002</DeviceType>
            <InternalPollInterval>15000</InternalPollInterval>
            <ReadTotalConsumption>MODBUSR(A, 0x0094, LittleEndianUint32) / 10</ReadTotalConsumption>
            <ReadDemand>(MODBUSR(A, 0x000A, UInt16) + MODBUSR(A, 0x000B, UInt16)) / 1000</ReadDemand>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Charger Use Mode</Name>
          <Id>-5</Id>
          <Model>ModbusMultiValueSwitch</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>2500</InternalPollInterval>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <OffStateValue>0</OffStateValue>
            <ReadSwitchState>MODBUSR(H, 0x008B, UInt16)</ReadSwitchState>
            <WriteSwitchState>#MODBUSW(SH, 0x001F, Uint16, Mu)</WriteSwitchState>
            <Value0>0</Value0>
            <Value0Index>0</Value0Index>
            <Value0Name>Self Use Mode</Value0Name>
            <Value0IconId>209</Value0IconId>
            <Value1>1</Value1>
            <Value1Index>1</Value1Index>
            <Value1Name>Feedin Priority</Value1Name>
            <Value1IconId>224</Value1IconId>
            <Value2>2</Value2>
            <Value2Index>2</Value2Index>
            <Value2Name>Backup Mode</Value2Name>
            <Value2IconId>235</Value2IconId>
            <Value3>3</Value3>
            <Value3Index>3</Value3Index>
            <Value3Name>Manual Mode</Value3Name>
            <Value3IconId>101</Value3IconId>
            <Value4Index>68</Value4Index>
            <Value5Index>69</Value5Index>
            <Value6Index>70</Value6Index>
            <Value7Index>71</Value7Index>
            <Value8Index>72</Value8Index>
            <Value9Index>73</Value9Index>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Purchase/Feed to Grid</Name>
          <Id>-6</Id>
          <Model>ModbusVariable</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>50000</InternalPollInterval>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <IsReadOnly>True</IsReadOnly>
            <ReadState>MODBUSR(A, 0x0046, LittleEndianInt32) / 1000</ReadState>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>PV1</Name>
          <Id>-7</Id>
          <Model>ModbusVariable</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>60000</InternalPollInterval>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <IsReadOnly>True</IsReadOnly>
            <ReadState>MODBUSR(A, 0x000A, UInt16) / 1000</ReadState>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>PV2</Name>
          <Id>-8</Id>
          <Model>ModbusVariable</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>15000</InternalPollInterval>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <IsReadOnly>True</IsReadOnly>
            <ReadState>MODBUSR(A, 0x000B, UInt16) / 1000</ReadState>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>SelfUse Night Charge</Name>
          <Id>-9</Id>
          <Model>ModbusDimmer</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>2500</InternalPollInterval>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <IconId>3019</IconId>
            <InitializeScript>#MODBUSWNE(SH, 0x0062, Uint16, 1);</InitializeScript>
            <ReadLevel>MODBUSR(H, 0x0094, UInt16) / 100</ReadLevel>
            <WriteLevel>#MODBUSW(SH, 0x0063, Uint16, Le * 100)</WriteLevel>
          </DeviceProperties>
        </Device>
        <Device>
          <Name>Total Energy</Name>
          <Id>-10</Id>
          <Model>ModbusVariable</Model>
          <DeviceProperties>
            <DeviceType>0</DeviceType>
            <InternalPollInterval>150000</InternalPollInterval>
            <ShouldDoPeriodicWrite>False</ShouldDoPeriodicWrite>
            <IsReadOnly>True</IsReadOnly>
            <ReadState>MODBUSR(A, 0x0052, LittleEndianUint32) / 10</ReadState>
          </DeviceProperties>
        </Device>
      </Devices>
    </Module>
  </Template>
</Templates>
