{"id":13060,"date":"2026-09-18T07:35:00","date_gmt":"2026-09-18T05:35:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=13060"},"modified":"2026-10-26T13:25:45","modified_gmt":"2026-10-26T12:25:45","slug":"tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/","title":{"rendered":"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP"},"content":{"rendered":"<p>A Modbus register is a numbered storage location holding 16 bits. SunSpec defines how groups of these registers describe measurements and device information. The model chain makes it possible to discover supported data blocks instead of copying an address from another inverter. <strong>Discovery still needs the correct device configuration and model definitions; it does not make every measurement available on every inverter.<\/strong><\/p>\n<p>This tutorial reads phase A current from integer inverter model 103. The Python example uses the PyModbus 3.16.1 interface checked on 24 October 2026. It performs holding-register reads only. Host, port and unit identifier are device-specific; enabling access and selecting the register map follow the manufacturer\u2019s documentation.<\/p>\n<h2>1. Finding the map with the correct address convention<\/h2><p>SunSpec\u2019s standard zero-based protocol addresses are 0, 40000 and 50000. The first two registers contain <code>0x5375<\/code> and <code>0x6E53<\/code>, together spelling <code>SunS<\/code>. These addresses are already protocol offsets.<\/p><p>A manual can use zero-based offsets, one-based register numbers or traditional 4xxxx holding-register references. Consequently, a printed \u201c40001\u201d cannot always be converted to 40000. In traditional reference notation, 40001 can mean the first holding register, whose protocol offset is 0. The documented convention and the marker must agree.<\/p><p>A meter and inverter may use separate unit identifiers, separate connections or different models in a shared map. Neither a second unit identifier nor a particular default setting is universal. A valid Modbus reply alone does not prove that the intended device answered.<\/p>\n<figure class=\"lw-diagram\"><img src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/quality-13060-20261024.png\" width=\"1120\" height=\"580\" loading=\"lazy\" decoding=\"async\" alt=\"Example SunSpec map with zero-based addresses, two-register headers and end model; phase A current and its validated signed scale factor\"><figcaption>Example addresses apply only to this model chain. Names and offsets require the corresponding model definition.<\/figcaption><\/figure>\n<h2>2. Following model headers<\/h2><p>After the two marker registers, each header contains a model ID and a length. The length counts data registers after the two-register header. The next header is therefore at <code>current_header + 2 + length<\/code>. End model <code>0xFFFF<\/code> has length 0.<\/p><p>Model IDs identify layouts, but names, units and point offsets come from the published model definitions, not from a complete dictionary transmitted by every device. Unknown models can be skipped using their lengths; known models still need an appropriate decoder.<\/p><ul><li><strong>1:<\/strong> common device identification.<\/li><li><strong>101\/102\/103:<\/strong> single-phase, split-phase and three-phase integer inverter models.<\/li><li><strong>111\/112\/113:<\/strong> corresponding floating-point model family, with a different layout.<\/li><li><strong>160:<\/strong> multiple-MPPT extension; input groups do not universally identify individual physical strings.<\/li><li><strong>201\u2013204:<\/strong> integer meter models for different wiring arrangements.<\/li><\/ul><p>The supported models and any integer\/float selection depend on the product. Model 102 means split phase, not an arbitrary two-phase installation. The example deliberately reports an error if exactly one model 103 is not present.<\/p>\n<h2>3. Consistent decoding of phase A current<\/h2><p>All offsets below are zero-based from the first data register, excluding ID and length: <code>A<\/code> is at 0, <code>AphA<\/code> at 1, <code>AphB<\/code> at 2, <code>AphC<\/code> at 3 and <code>A_SF<\/code> at 4. If the data starts at 40071, phase A is at 40072 and the scale factor at 40075. That is a separation of three addresses.<\/p><p>The current is unsigned; the scale factor is a signed decimal exponent. Raw current 1234 and exponent \u22122 give <code>1234 \u00d7 10^-2 = 12.34 A<\/code>. A different exponent gives a different multiplier. Missing current <code>0xFFFF<\/code> or missing scale factor <code>0x8000<\/code> must not be converted into a measurement.<\/p>\n<h2>4. Reading with bounded discovery and error checks<\/h2><p>The following example checks connection, response length, model boundaries, the end marker and both invalid-value markers. It uses <code>device_id<\/code>, as required by the documented PyModbus interface; older examples using <code>slave<\/code> need version-specific adaptation.<\/p>\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"python\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Python<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-python\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">from<\/span> pymodbus.client <span class=\"cbb-keyword\">import<\/span> ModbusTcpClient\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">from<\/span> pymodbus.exceptions <span class=\"cbb-keyword\">import<\/span> ModbusException\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">def<\/span> <span class=\"cbb-function\">read_words<\/span>(client, address, count, unit):\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> <span class=\"cbb-keyword\">not<\/span> <span class=\"cbb-number\">1<\/span> &lt;= count &lt;= <span class=\"cbb-number\">125<\/span> <span class=\"cbb-keyword\">or<\/span> <span class=\"cbb-keyword\">not<\/span> <span class=\"cbb-number\">0<\/span> &lt;= address &lt;= <span class=\"cbb-number\">65536<\/span> - count:\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">ValueError<\/span>(<span class=\"cbb-string\">\"Invalid register range\"<\/span>)\n<\/span><span class=\"cbb-line\">    response = client.<span class=\"cbb-function\">read_holding_registers<\/span>(\n<\/span><span class=\"cbb-line\">        address, count=count, device_id=unit\n<\/span><span class=\"cbb-line\">    )\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> response.<span class=\"cbb-function\">isError<\/span>():\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">f\"Modbus error at {address}: {response}\"<\/span>)\n<\/span><span class=\"cbb-line\">    words = response.registers\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> <span class=\"cbb-function\">len<\/span>(words) != count <span class=\"cbb-keyword\">or<\/span> <span class=\"cbb-function\">any<\/span>(<span class=\"cbb-keyword\">not<\/span> <span class=\"cbb-number\">0<\/span> &lt;= x &lt;= <span class=\"cbb-number\">65535<\/span> <span class=\"cbb-keyword\">for<\/span> x <span class=\"cbb-keyword\">in<\/span> words):\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"Incomplete or invalid response\"<\/span>)\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">return<\/span> words\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">def<\/span> <span class=\"cbb-function\">find_base<\/span>(client, unit):\n<\/span><span class=\"cbb-line\">    failures = &#91;&#93;\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">for<\/span> base <span class=\"cbb-keyword\">in<\/span> (<span class=\"cbb-number\">40000<\/span>, <span class=\"cbb-number\">0<\/span>, <span class=\"cbb-number\">50000<\/span>):\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">try<\/span>:\n<\/span><span class=\"cbb-line\">            <span class=\"cbb-keyword\">if<\/span> <span class=\"cbb-function\">read_words<\/span>(client, base, <span class=\"cbb-number\">2<\/span>, unit) == &#91;<span class=\"cbb-number\">0x5375<\/span>, <span class=\"cbb-number\">0x6E53<\/span>&#93;:\n<\/span><span class=\"cbb-line\">                <span class=\"cbb-keyword\">return<\/span> base\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">except<\/span> (ModbusException, OSError, RuntimeError) <span class=\"cbb-keyword\">as<\/span> exc:\n<\/span><span class=\"cbb-line\">            failures.<span class=\"cbb-function\">append<\/span>(<span class=\"cbb-string\">f\"{base}: {exc}\"<\/span>)\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"No SunS marker found; \"<\/span> + <span class=\"cbb-string\">\"; \"<\/span>.<span class=\"cbb-function\">join<\/span>(failures))\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">def<\/span> <span class=\"cbb-function\">discover<\/span>(client, base, unit):\n<\/span><span class=\"cbb-line\">    position = base + <span class=\"cbb-number\">2<\/span>\n<\/span><span class=\"cbb-line\">    models = &#91;&#93;\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">for<\/span> _ <span class=\"cbb-keyword\">in<\/span> <span class=\"cbb-function\">range<\/span>(<span class=\"cbb-number\">256<\/span>):\n<\/span><span class=\"cbb-line\">        model, length = <span class=\"cbb-function\">read_words<\/span>(client, position, <span class=\"cbb-number\">2<\/span>, unit)\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">if<\/span> model == <span class=\"cbb-number\">0xFFFF<\/span>:\n<\/span><span class=\"cbb-line\">            <span class=\"cbb-keyword\">if<\/span> length != <span class=\"cbb-number\">0<\/span>:\n<\/span><span class=\"cbb-line\">                <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"Invalid end model length\"<\/span>)\n<\/span><span class=\"cbb-line\">            <span class=\"cbb-keyword\">return<\/span> models\n<\/span><span class=\"cbb-line\">        next_position = position + <span class=\"cbb-number\">2<\/span> + length\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">if<\/span> model == <span class=\"cbb-number\">0<\/span> <span class=\"cbb-keyword\">or<\/span> length == <span class=\"cbb-number\">0<\/span> <span class=\"cbb-keyword\">or<\/span> next_position &gt; <span class=\"cbb-number\">65534<\/span>:\n<\/span><span class=\"cbb-line\">            <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"Invalid model header or address range\"<\/span>)\n<\/span><span class=\"cbb-line\">        models.<span class=\"cbb-function\">append<\/span>((model, position + <span class=\"cbb-number\">2<\/span>, length))\n<\/span><span class=\"cbb-line\">        position = next_position\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"Model scan limit reached\"<\/span>)\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">def<\/span> <span class=\"cbb-function\">phase_a_current<\/span>(client, models, unit):\n<\/span><span class=\"cbb-line\">    matches = &#91;m <span class=\"cbb-keyword\">for<\/span> m <span class=\"cbb-keyword\">in<\/span> models <span class=\"cbb-keyword\">if<\/span> m&#91;<span class=\"cbb-number\">0<\/span>&#93; == <span class=\"cbb-number\">103<\/span>&#93;\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> <span class=\"cbb-function\">len<\/span>(matches) != <span class=\"cbb-number\">1<\/span>:\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"Exactly one model 103 required\"<\/span>)\n<\/span><span class=\"cbb-line\">    _, start, length = matches&#91;<span class=\"cbb-number\">0<\/span>&#93;\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> length &lt; <span class=\"cbb-number\">5<\/span>:\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"Model 103 is too short\"<\/span>)\n<\/span><span class=\"cbb-line\">    block = <span class=\"cbb-function\">read_words<\/span>(client, start, <span class=\"cbb-number\">5<\/span>, unit)\n<\/span><span class=\"cbb-line\">    raw, sf_raw = block&#91;<span class=\"cbb-number\">1<\/span>&#93;, block&#91;<span class=\"cbb-number\">4<\/span>&#93;\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> raw == <span class=\"cbb-number\">0xFFFF<\/span> <span class=\"cbb-keyword\">or<\/span> sf_raw == <span class=\"cbb-number\">0x8000<\/span>:\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">return<\/span> <span class=\"cbb-constant\">None<\/span>\n<\/span><span class=\"cbb-line\">    exponent = sf_raw - <span class=\"cbb-number\">65536<\/span> <span class=\"cbb-keyword\">if<\/span> sf_raw &gt;= <span class=\"cbb-number\">32768<\/span> <span class=\"cbb-keyword\">else<\/span> sf_raw\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">if<\/span> <span class=\"cbb-keyword\">not<\/span> <span class=\"cbb-number\">-10<\/span> &lt;= exponent &lt;= <span class=\"cbb-number\">10<\/span>:\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"Invalid SunSpec scale factor\"<\/span>)\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">return<\/span> raw * (<span class=\"cbb-number\">10.0<\/span> ** exponent)\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">def<\/span> <span class=\"cbb-function\">main<\/span>():\n<\/span><span class=\"cbb-line\">    unit = <span class=\"cbb-number\">1<\/span>  <span class=\"cbb-comment\"># Device-specific configuration<\/span>\n<\/span><span class=\"cbb-line\">    client = <span class=\"cbb-function\">ModbusTcpClient<\/span>(<span class=\"cbb-string\">\"192.168.1.50\"<\/span>, port=<span class=\"cbb-number\">502<\/span>, timeout=<span class=\"cbb-number\">3<\/span>)\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">try<\/span>:\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-keyword\">if<\/span> <span class=\"cbb-keyword\">not<\/span> client.<span class=\"cbb-function\">connect<\/span>():\n<\/span><span class=\"cbb-line\">            <span class=\"cbb-keyword\">raise<\/span> <span class=\"cbb-function\">RuntimeError<\/span>(<span class=\"cbb-string\">\"Connection failed\"<\/span>)\n<\/span><span class=\"cbb-line\">        base = <span class=\"cbb-function\">find_base<\/span>(client, unit)\n<\/span><span class=\"cbb-line\">        models = <span class=\"cbb-function\">discover<\/span>(client, base, unit)\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-function\">print<\/span>(<span class=\"cbb-string\">\"Base:\"<\/span>, base, <span class=\"cbb-string\">\"Models:\"<\/span>, models)\n<\/span><span class=\"cbb-line\">        current = <span class=\"cbb-function\">phase_a_current<\/span>(client, models, unit)\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-function\">print<\/span>(<span class=\"cbb-string\">\"No valid phase A current\"<\/span> <span class=\"cbb-keyword\">if<\/span> current <span class=\"cbb-keyword\">is<\/span> <span class=\"cbb-constant\">None<\/span>\n<\/span><span class=\"cbb-line\">              <span class=\"cbb-keyword\">else<\/span> <span class=\"cbb-string\">f\"{current:.2f} A\"<\/span>)\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-keyword\">finally<\/span>:\n<\/span><span class=\"cbb-line\">        client.<span class=\"cbb-function\">close<\/span>()\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\">\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">if<\/span> __name__ == <span class=\"cbb-string\">\"__main__\"<\/span>:\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-function\">main<\/span>()<\/span><\/code><\/pre><\/figure>\n<p>The scan limit of 256 headers is a guard in this example, not a SunSpec model-count limit. A missing marker, missing model or invalid reply stops the calculation instead of printing a plausible value. The example handles one model-103 instance; multi-instance devices need explicit selection. Tests used a simulated client, including alternate bases, missing values and malformed headers; no inverter was contacted.<\/p>\n<h2>Other types and polling<\/h2><p>SunSpec specifies high-word-first order for multi-register values. Modbus itself defines register transfer, not every manufacturer\u2019s multiword layout. Signed values need two\u2019s-complement decoding; floating-point values need their specified IEEE layout and invalid-value handling. Text occupies a fixed-size register area and may be null-terminated or null-padded.<\/p><p>Invalid patterns depend on the SunSpec type: for example, <code>int16<\/code> uses <code>0x8000<\/code>, <code>uint16<\/code> uses <code>0xFFFF<\/code>, and <code>acc32<\/code> uses zero as \u201cnot accumulated\u201d. An <code>acc32<\/code> is not interchangeable with <code>uint32<\/code> merely because both occupy two registers.<\/p><p>One block request reduces round trips but does not guarantee a simultaneous physical sample. Function 03 permits at most 125 registers per request; a device may impose a smaller limit. Polling frequency, connection limits and update cadence must follow the device documentation. No universal five-second interval is guaranteed safe for every product or shared gateway.<\/p>\n<h2>Home Assistant: a clearly scoped example<\/h2><p>This configuration applies only to the illustrated map: model-103 data begins at 40071 and <code>A_SF = -2<\/code>. The fixed <code>scale: 0.01<\/code> is a multiplier, not the exponent. Home Assistant does not automatically discover this SunSpec model or its scale factor from that YAML.<\/p>\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"yaml\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">YAML<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-yaml\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-attr\">modbus<\/span>:\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-operator\">-<\/span> <span class=\"cbb-attr\">name<\/span>: inverter\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-attr\">type<\/span>: tcp\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-attr\">host<\/span>: 192.168.1.50\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-attr\">port<\/span>: <span class=\"cbb-number\">502<\/span>\n<\/span><span class=\"cbb-line\">    <span class=\"cbb-attr\">sensors<\/span>:\n<\/span><span class=\"cbb-line\">      <span class=\"cbb-operator\">-<\/span> <span class=\"cbb-attr\">name<\/span>: <span class=\"cbb-string\">\"Phase A current\"<\/span>\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">slave<\/span>: <span class=\"cbb-number\">1<\/span>\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">address<\/span>: <span class=\"cbb-number\">40072<\/span>\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">input_type<\/span>: holding\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">data_type<\/span>: uint16\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">nan_value<\/span>: <span class=\"cbb-number\">65535<\/span>\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">scale<\/span>: <span class=\"cbb-number\">0.01<\/span>\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">precision<\/span>: <span class=\"cbb-number\">2<\/span>\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">unit_of_measurement<\/span>: <span class=\"cbb-string\">\"A\"<\/span>\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">device_class<\/span>: current\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">state_class<\/span>: measurement\n<\/span><span class=\"cbb-line\">        <span class=\"cbb-attr\">scan_interval<\/span>: <span class=\"cbb-number\">10<\/span><\/span><\/code><\/pre><\/figure>\n<p><code>nan_value<\/code> excludes the raw current sentinel and produces <code>unknown<\/code> according to the current integration documentation. It cannot validate the separate scale-factor register. The fixed configuration is suitable only while the verified map and factor remain valid; rediscovery and comparison are appropriate after firmware or model-configuration changes.<\/p><p>Power in watts uses <code>device_class: power<\/code> and <code>state_class: measurement<\/code>. Energy totals use <code>device_class: energy<\/code>, a supported energy unit, and <code>total<\/code> or <code>total_increasing<\/code> according to reset behavior. Current Home Assistant energy views can also use power measurements. Energy can also be calculated from power over time: a constant 1 kW over two hours gives 2 kWh. For changing power, accuracy depends on sampling and the calculation method.<\/p>\n<div class=\"lw-faq\"><h2>Questions and answers<\/h2><h3>Why is the scale factor part of the model?<\/h3><p>A signed exponent trades numerical range for resolution. For a valid int16 power value, exponent 0 allows up to 32767 W, while positive exponents extend the range in larger steps. The referenced SunSpec model specification describes scale-factor points as static; changes in device configuration or firmware still warrant checking the resulting map.<\/p><h3>What can corrupt energy statistics?<\/h3><p>An invalid zero followed by the real lifetime total can be misinterpreted as a reset and a large new increase when using <code>total_increasing<\/code>. The point type and reset behavior therefore matter. For a model-defined accumulator, an invalid raw zero needs exclusion; that rule must not be copied blindly to unrelated sensors where zero is valid. Unknown data should remain unknown rather than becoming an artificial zero.<\/p><h3>How should access be restricted?<\/h3><p>Traditional Modbus TCP on port 502 has no built-in authentication or encryption. Modbus Security is a separate TLS-based protocol and requires device support. This tutorial needs only read access. A restricted network path and no public port forwarding reduce exposure. A routed firewall normally does not control direct same-subnet traffic; host firewalls, switch ACLs and client isolation can still restrict that traffic.<\/p><\/div>\n<div class=\"lw-quellen\"><h2>Sources<\/h2><ul><li><a href=\"https:\/\/sunspec.org\/wp-content\/uploads\/2021\/02\/SunSpec-Device-Information-Model-Specificiation-V1-0-02-01-2021.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">SunSpec Device Information Model Specification, 2021 draft<\/a><\/li><li><a href=\"https:\/\/sunspec.org\/wp-content\/uploads\/2019\/08\/SunSpec-Information-Models-12041.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">SunSpec Information Model Specification 1.9<\/a><\/li><li><a href=\"https:\/\/raw.githubusercontent.com\/sunspec\/models\/master\/json\/model_103.json\" target=\"_blank\" rel=\"noopener noreferrer\">SunSpec model 103 definition<\/a><\/li><li><a href=\"https:\/\/raw.githubusercontent.com\/sunspec\/models\/master\/json\/model_160.json\" target=\"_blank\" rel=\"noopener noreferrer\">SunSpec model 160 definition<\/a><\/li><li><a href=\"https:\/\/pymodbus.readthedocs.io\/en\/stable\/source\/client.html\" target=\"_blank\" rel=\"noopener noreferrer\">PyModbus 3.16.1 client interface<\/a><\/li><li><a href=\"https:\/\/www.home-assistant.io\/integrations\/modbus\/\" target=\"_blank\" rel=\"noopener noreferrer\">Home Assistant Modbus integration<\/a><\/li><li><a href=\"https:\/\/www.home-assistant.io\/docs\/energy\/faq\/\" target=\"_blank\" rel=\"noopener noreferrer\">Home Assistant energy FAQ<\/a><\/li><li><a href=\"https:\/\/contributors.home-assistant.io\/core\/entities\/sensor\/\" target=\"_blank\" rel=\"noopener noreferrer\">Home Assistant sensor state classes<\/a><\/li><li><a href=\"https:\/\/www.home-assistant.io\/integrations\/integration\/\" target=\"_blank\" rel=\"noopener noreferrer\">Home Assistant Integral<\/a><\/li><li><a href=\"https:\/\/www.modbus.org\/file\/secure\/modbusprotocolspecification.pdf\" target=\"_blank\" rel=\"noopener noreferrer\">Modbus Application Protocol V1.1b3<\/a><\/li><li><a href=\"https:\/\/www.modbus.org\/modbus-specifications\" target=\"_blank\" rel=\"noopener noreferrer\">Modbus and Modbus Security specifications<\/a><\/li><li><a href=\"https:\/\/icdocs.ismacontrolli.com\/aac20-modbus\/V1.10\/list-of-modbus-registers\" target=\"_blank\" rel=\"noopener noreferrer\">iSMA CONTROLLI: Modbus address notation example<\/a><\/li><\/ul><\/div>\n","protected":false},"excerpt":{"rendered":"<p>SunSpec model discovery locates supported register blocks. This tutorial reads phase A current from model 103, distinguishes address conventions, checks missing values and scale factors, and derives a clearly scoped Home Assistant configuration.<\/p>\n","protected":false},"author":1,"featured_media":23772,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[92624],"tags":[91371,91365,91657,91362,91219],"class_list":["post-13060","post","type-post","status-publish","format-standard","hentry","category-tutorials-en-smart-home","tag-energy-management","tag-home-assistant","tag-modbus","tag-photovoltaics","tag-tutorial"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Finding the SunS marker, walking the model chain, decoding scale factors and data types, and turning the result into a Home Assistant sensor.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Finding the SunS marker, walking the model chain, decoding scale factors and data types, and turning the result into a Home Assistant sensor.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-18T05:35:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-26T12:25:45+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/quality-hero-13060-20261024.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP\",\"datePublished\":\"2026-09-18T05:35:00+00:00\",\"dateModified\":\"2026-10-26T12:25:45+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/\"},\"wordCount\":1103,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/quality-hero-13060-20261024.png\",\"keywords\":[\"Energy Management\",\"Home Assistant\",\"Modbus\",\"Photovoltaics\",\"Tutorial\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/\",\"name\":\"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/quality-hero-13060-20261024.png\",\"datePublished\":\"2026-09-18T05:35:00+00:00\",\"dateModified\":\"2026-10-26T12:25:45+00:00\",\"description\":\"Finding the SunS marker, walking the model chain, decoding scale factors and data types, and turning the result into a Home Assistant sensor.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/quality-hero-13060-20261024.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/quality-hero-13060-20261024.png\",\"width\":1200,\"height\":630,\"caption\":\"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/smart-home\\\/tutorials-en-smart-home\\\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP | Lukas Wojcik","description":"Finding the SunS marker, walking the model chain, decoding scale factors and data types, and turning the result into a Home Assistant sensor.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/","og_locale":"en_US","og_type":"article","og_title":"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP | Lukas Wojcik","og_description":"Finding the SunS marker, walking the model chain, decoding scale factors and data types, and turning the result into a Home Assistant sensor.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-09-18T05:35:00+00:00","article_modified_time":"2026-10-26T12:25:45+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/quality-hero-13060-20261024.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP","datePublished":"2026-09-18T05:35:00+00:00","dateModified":"2026-10-26T12:25:45+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/"},"wordCount":1103,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/quality-hero-13060-20261024.png","keywords":["Energy Management","Home Assistant","Modbus","Photovoltaics","Tutorial"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/","name":"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/quality-hero-13060-20261024.png","datePublished":"2026-09-18T05:35:00+00:00","dateModified":"2026-10-26T12:25:45+00:00","description":"Finding the SunS marker, walking the model chain, decoding scale factors and data types, and turning the result into a Home Assistant sensor.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/quality-hero-13060-20261024.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/quality-hero-13060-20261024.png","width":1200,"height":630,"caption":"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/smart-home\/tutorials-en-smart-home\/tutorial-reading-sunspec-registers-from-a-pv-inverter-over-modbus-tcp\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: Reading SunSpec Registers From a PV Inverter Over Modbus TCP"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13060","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=13060"}],"version-history":[{"count":5,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13060\/revisions"}],"predecessor-version":[{"id":27372,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13060\/revisions\/27372"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/23772"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=13060"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=13060"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=13060"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}