Studer-Innotec solar components integration

I would like to have an integration for Studer-Innotec solar components to be able to read whats happening on the DC side like power from the panels, power into or out of the batteries, and battery state of charge.

Open Hab has an integration. Is there a way to get HA to read from Open Hab if I have to go that route?

Hi,

I found that

It seems they’re using modbus, no API

And here you can find all the modbus addresses

https://technext3.studer-innotec.com/web/content/109002?unique=c4a5d142c63dcb59ab307617309947a16ac0f8d7&download=true#page142

But maybe someone can make a fancy integration simplifing everything?

2 Likes

I would love to have a Integration here too….

1 Like

So far, no Studer integration, but using RESTful sensors, can query the Studer Portal for inverter power, power into and out of the batteries, and battery state of charge. Essentially can see that same information as the Studer web app, but sometimes the portal doesn’t respond (same as what occurs with the web and Android apps).

2023-10-07: This code has been replaced by newer code. Please see the updated code at the end of this post.

# Studer Synoptic Data Request
rest:
  - authentication: basic
    method: GET
    scan_interval: 10
    timeout: 120
    resource: https://api.studer-innotec.com/api/v1/installation/synoptic/NNNN #where NNNN is your installationNumber
    headers:
      Accept: application/json
      PHASH: pppppppppppppppppppppppppppppp #where pppppppppppppppppppppppppppppp is your #MD5 encoded password, or put it in the secrets.yaml file 
      UHASH: uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu #where uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu is your #SHA256 encoded username, or put it in the secrets.yaml file 
    sensor:
      - name: "Studer"
        unique_id: "studer"
        value_template: "42"
        json_attributes:
          - energy
          - battery
          - power


# Studer Aux contact status read single Info data point using a different API from example in https://community.home-assistant.io/t/read-studer-parameters-via-xcom-lan-and-rest-sensor/597933
  - authentication: basic
    method: GET
    scan_interval: 30
    timeout: 120
    resource: "https://portal.studer-innotec.com/scomwebservice.asmx/ReadUserInfo?email=<your email>&pwd=<your Studer portal password>&installationNumber=<your system ID number>&device=XT1&infoId=3031"
    sensor:
      - name: "Studer Xtender AUX1"
        unique_id: "studer_xtender_aux1"
        value_template: "{{ 'unavailable' if (value_json.UserInfoResult.ErrorCode!='1') else 'opened' if (value_json.UserInfoResult.UIntValue=='0') else 'closed' }}"
        json_attributes_path: "$['UserInfoResult']"
        json_attributes:
          - UIntValue
          - FloatValue
          - ErrorCode
          - ErrorMessage
          - ScomFormatNo


template:
  - binary_sensor:
      - name: "Is DST"
        state: "{{ now().timetuple().tm_isdst == True }}"


  - sensor:
      - name: "Studer Cloud Status"
        unique_id: "studer.cloud_status"
        state: >-
          {% set x = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if x == True %}
            {{'Studer Cloud Online'}}
          {% else %}
            {{'Studer Cloud OFFLINE'}}
          {% endif %}

      - name: "Solar Production"
        unit_of_measurement: "kWh"
        unique_id: "studer.solar_prod"
        device_class: energy
        state_class: total_increasing
        state: >-
          {% set x = state_attr('sensor.studer', 'energy')['solar']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{x}}
          {% else %}
            {{ states('sensor.solar_prod') }}
          {% endif %}

      - name: "Grid Consumption"
        state: "{{ state_attr('sensor.studer', 'energy')['gridGenset'] }}"
        unit_of_measurement: "kWh"
        unique_id: "studer.grid_prod"
        device_class: energy
        state_class: total_increasing

      - name: "Battery Charge Ah"
        unit_of_measurement: "Ah"
        unique_id: "studer.batt_charge"
        device_class: energy
        state_class: total_increasing
        state: >-
          {% set x = state_attr('sensor.studer', 'energy')['batteryCharge']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{x}}
          {% else %}
            {{ states('sensor.batt_charge') }}
          {% endif %}

      - name: "Battery Discharge Ah"
        unit_of_measurement: "Ah"
        unique_id: "studer.batt_discharge"
        device_class: energy
        state_class: total_increasing
        state: >-
          {% set x = state_attr('sensor.studer', 'energy')['batteryDischarge']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{x}}
          {% else %}
            {{ states('sensor.batt_discharge') }}
          {% endif %}

      - name: "Battery Power In"
        unit_of_measurement: "kW"
        device_class: power
        state_class: measurement
        state: >
          {% set power = states('sensor.battpower' )|float(0) %}
          {% if power >= 0 %}
            {{ power }}
          {% else %}
            0
          {% endif %}

      - name: "Battery Power Out"
        unit_of_measurement: "kW"
        device_class: power
        state_class: measurement
        state: >
          {% set power = states('sensor.battpower' )|float(0) %}
          {% if power < 0 %}
            {{ power|abs }}
          {% else %}
            0
          {% endif %}

      - name: "Battery SOC Percent"
        unique_id: "studer.battsoc"
        unit_of_measurement: "%"
        state: >-
          {% set soc = state_attr('sensor.studer', 'battery')['soc']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{soc}}
          {% else %}
            {{ states('sensor.battsoc') }}
          {% endif %}

      - name: "Battery Power"
        unique_id: "studer.battpower"
        unit_of_measurement: "kW"
        device_class: energy
        state: >-
          {% set power = state_attr('sensor.studer', 'battery')['power']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{ power }}
          {% else %}
            {{ states('sensor.battpower') }}
          {% endif %}

      - name: "Battery Temperature"
        unique_id: "studer.batttemp"
        unit_of_measurement: "°C"
        state: >-
          {% set temperature = state_attr('sensor.studer', 'battery')['temperature']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{ temperature }}
          {% else %}
            {{ states('sensor.batttemp') }}
          {% endif %}

      - name: "Battery Voltage"
        unique_id: "studer.battvoltage"
        unit_of_measurement: "V"
        state: >-
          {% set voltage = state_attr('sensor.studer', 'battery')['voltage']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{ voltage }}
          {% else %}
            {{ states('sensor.battvoltage') }}
          {% endif %}

      - name: "PV Generation"
        unique_id: "studer.solarpower"
        unit_of_measurement: "kW"
        state: >-
          {% set solar_power = state_attr('sensor.studer', 'power')['solar']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{ solar_power }}
          {% else %}
            {{ states('sensor.solarpower') }}
          {% endif %}

      - name: "Grid Consumption"
        unique_id: "studer.gridpower"
        unit_of_measurement: "kW"
        state: >-
          {% set grid_power = state_attr('sensor.studer', 'power')['gridGenset']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{ grid_power }}
          {% else %}
            {{ states('sensor.gridpower') }}
          {% endif %}

      - name: "House Consumption"
        unique_id: "studer.houseload"
        unit_of_measurement: "kW"
        state: >-
          {% set house_load = state_attr('sensor.studer', 'power')['load']|float(0) %}
          {% set its_alive = state_attr('sensor.studer', 'energy')['hasInverter'] %}
          {% if its_alive == True %}
            {{ house_load }}
          {% else %}
            {{ states('sensor.houseload') }}
          {% endif %}

Hi guys.
I’m new to home assistant.
I have HA running and I’m trying to add the studer code lines to configuration.yaml.
After pasting in Studio Code Server, I have several errors … Can someone give some help?
I’ve coded C and basic 2 decades ago, but haven’t integrated yet in HA …
Thanks

I’ve managed to solve the identation problem … But now I have these:

Check and Restart
A basic validation of the configuration is automatically done before restarting. The basic validation ensures the YAML configuration doesn’t have errors which will prevent Home Assistant or any integration from starting. It’s also possible to only do the basic validation check without restarting.

Configuration invalid!
Integration error: headers - Integration ‘headers’ not found.
Integration error: platform - Integration ‘platform’ not found.
Integration error: json_attributes - Integration ‘json_attributes’ not found.
Integration error: method - Integration ‘method’ not found.
Integration error: scan_interval - Integration ‘scan_interval’ not found.
Integration error: resource - Integration ‘resource’ not found.
Integration error: name - Integration ‘name’ not found.
Integration error: value_template - Integration ‘value_template’ not found.

Can someone give me some hints?

Thanks in advance.

You will probably want to break the problem into two parts.

  1. the RESTful json API calls
  2. the Studer sensors

Delete the Studer sensor lines and focus on getting the RESTful json API calls to run without errors first.

Before working on the yaml, you will want to confirm that your MD5 hash of password and SHA256 hash of e-mail address (the encrypted versions of the credentials you use when logging into the Studer portal) along with the installation number are correct. You can confirm the information using this API test site:
https://api.studer-innotec.com/swagger/ui/index#!/Installation/Installation_Synoptic

After putting the information in that site, it should report back the measurements from your installation as long as the information that you provide is correct and the Studer portal is working correctly at that moment.

UPDATED 2023-10-07

I have reorganized and cleaned up the code for the RESTful API calls to the Studer cloud along with the various Studer sensors. Previously I had everything in the configuration.yaml file, and now only the RESTful API calls are there. The RESTful user name and password have been moved to the secrets.yaml file, and the Studer sensor files have been moved to a studer.yaml file. Also the scan interval is set to a very large number and the actual scans are initiated by an automation to control which API requests are made at any moment to avoid overwhelming the portal.

RESTful Studer API calls:
I have five Studer API calls. The first one requests several Studer performance data points in one call. The others request a single Studer data points. The single data point ones are based on one that can be found here: Read Studer parameters via Xcom-LAN and Rest Sensor

This is the new configuration.yaml file:

#CONFIGURATION.YAML yaml file

# Loads default set of integrations. Do not remove.
default_config:

# Line to enable HA Configuration Editor
config_editor:

# Text to speech
tts:
  - platform: google_translate
  
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
http:
  use_x_forwarded_for: true
  trusted_proxies: 127.0.0.1

binary_sensor:

# RESTful INTEGRATIONS
rest:

# Studer API calls are made using RESTful integration blocks of code
# The RESTful integration blocks of code are called by a service in an automation
# The autoation is triggered by a timer and increments a counter that is used to select which RESTful integration block is called.
# The Synoptic data block is called when the counter is (>0 AND <5) OR (>5 AND <10) OR (>10 AND <15) OR (>15 AND <20)
# The AUX 1 data block is called when the counter is <1 
# The Battery Temperature data block is called when the counter is >4 AND <6
# The AC Input Voltage data block is called when the counter is >9 AND <11
# The Battery Voltage data block is called when the counter is >14 AND <16

# In each block, replace NNNN with your four digit System ID nummber
# In each block, replace !secret UHASH with your own Studer portal email address hashed in SHA256 either hardcoded in this yaml file or in your secrets.yaml file
# In each block, replace !secret PHASH with your own Studer portal password hashed in MD5 either hardcoded in this yaml file or in your secrets.yaml file

# Studer Synoptic Data Request
  - authentication: basic
    method: GET
    scan_interval: 99999
    timeout: 120
    resource: "https://api.studer-innotec.com/api/v1/installation/synoptic/NNNN"
    headers:
      Accept: application/json
      UHASH: !secret UHASH      
      PHASH: !secret PHASH
    sensor:
      - name: "Studer Synoptic"
        unique_id: "studer_synoptic"
        value_template: "42"
        json_attributes:
          - "energy"
          - "battery"
          - "power"



# Studer Aux contact data request
  - authentication: basic
    method: GET
    scan_interval: 99999
    timeout: 120
    resource: "https://api.studer-innotec.com/api/v1/installation/user-info/NNNN?device=XT_Group&infoId=3031"
    headers:
      Accept: application/json
      UHASH: !secret UHASH      
      PHASH: !secret PHASH
    sensor:
      - name: "Studer Xtender AUX1"
        unique_id: "studer_xtender_aux1"
        value_template: "44"
        json_attributes:
          - "status"
          - "uIntValue"
          - "floatValue"

# Studer Electronic Temperature data request
  - authentication: basic
    method: GET
    scan_interval: 99999
    timeout: 120
    resource: "https://api.studer-innotec.com/api/v1/installation/user-info/NNNN?device=XT_Group&infoId=3106"
    headers:
      Accept: application/json
      UHASH: !secret UHASH      
      PHASH: !secret PHASH
    sensor:
      - name: "Studer Xtender Electronic Temperature"
        unique_id: "studer_xtender_electronic_temperature"
        value_template: "46"
        json_attributes:
          - "status"
          - "uIntValue"
          - "floatValue"

# Studer AC Input Voltage data request
  - authentication: basic
    method: GET
    scan_interval: 99999
    timeout: 120
    resource: "https://api.studer-innotec.com/api/v1/installation/user-info/NNNN?device=XT_Group&infoId=3011"
    headers:
      Accept: application/json
      UHASH: !secret UHASH      
      PHASH: !secret PHASH
    sensor:
      - name: "Studer Xtender AC Input Voltage"
        unique_id: "studer_xtender_ac_input_voltage"
        value_template: "48"
        json_attributes:
          - "status"
          - "uIntValue"
          - "floatValue"

# Studer Battery Voltage data request
  - authentication: basic
    method: GET
    scan_interval: 99999
    timeout: 120
    resource: "https://api.studer-innotec.com/api/v1/installation/user-info/NNNN?device=XT_Group&infoId=3000"
    headers:
      Accept: application/json
      UHASH: !secret UHASH      
      PHASH: !secret PHASH
    sensor:
      - name: "Studer Xtender Battery Voltage"
        unique_id: "studer_xtender_battery_voltage"
        value_template: "50"
        json_attributes:
          - "status"
          - "uIntValue"
          - "floatValue"
          

# TEMPLATE INTEGRATION    
template:

#sensor to identify if daylight savings for selecting summer or winter tariff structure  
  - binary_sensor:
      - name: "Is DST"
        state: "{{ now().timetuple().tm_isdst == true }}"

        
#INCLUDE for tariffs sensor calculations
  - sensor: !include tariffs.yaml

#INCLUDE for Studer sensors that copy data from RESTful API call attributes 
  - sensor: !include studer.yaml  

#INCLUDE for Shelly sensor calculations 
  - sensor: !include shelly.yaml  

#INCLUDE for Tuya sensor yaml blocks 
  - sensor: !include tuya.yaml  

Next there is a secrets.yaml file which holds the credentials:

# SECRETS.YAML yaml secrets file
# Use this file to store secrets like usernames and passwords.
# Learn more at https://www.home-assistant.io/docs/configuration/secrets/

#Studer Portal and API credentials
# Where "PHASH" is the password hashed in MD5
# Where "UHASH" is the email address hashed in SHA256
# Where "userLevelCode" is the code required by the API for Installer QSP level access

UHASH: #place your SHA256 encoded username here. It should be a hex code that looks like: 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
PHASH: #place your MD5 encoded password here.  It should be a hex code that looks like: 79054025255fb1a26e4bc422aef54eb4

Before moving on to the next step which adds the individual Studer sensors, inspect the attributes that are created by the two RESTful calls. They should look like this:

![Single point api attributes when Studer portal is responding|472x361]image

If you aren’t seeing the attribute list it could be something wrong with the way you have entered your credentials, or that the Studer portal is currently having problems.
You can confirm your credentials for the Synoptic data API call here: Studer Web API

Also be aware that the status can show as “unknown” when the Studer portal is not responding. The portal frequently has problems, so if you see “unknown”, check if the portal is having problems by checking the portal’s dashboard in a web browser or using the Studer Android app. If either of those isn’t able to display the data, the RESTful API calls will also have problems. On startup, several of the sensor values can be displayed as “unknown” until new values come in. This can also happen if there isn’t any activity on the sensors due to no solar production, or battery charging or discharging.

1 Like

UPDATED 2023-10-07

This is the code for the Studer sensors. There is logic in the Studer sensor code for the Synoptic data that when there portal is not responding with new data, the sensor just repeats with the previous data. A similar test could probably be used for the single point data API call referencing either the ErrorCode or ErrorMessage attribute

# STUDER.YAML Studer yaml file
#Read Studer data from RESTful attributes and copy to individual sensors.  If the Studer portal didn't respond, repeat with the previous value

# This block tests if the Studer portal is responding with Synoptic data.
- name: "Studer Cloud Status"
  unique_id: "studer.cloud_status"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{'Studer Cloud Online'}}
    {% else %}
      {{'Studer Cloud OFFLINE'}}
    {% endif %}

- name: "Solar Production"
  unique_id: "studer.solar_prod"
  unit_of_measurement: "kWh"
  device_class: "energy"
  state_class: "total_increasing"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'energy')['solar']|float(0)}}
    {% else %}
      {{ states('sensor.solar_prod') }}
    {% endif %}

- name: "Grid Consumption"
  unique_id: "studer.grid_prod"
  unit_of_measurement: "kWh"
  device_class: "energy"
  state_class: "total_increasing"  
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'energy')['gridGenset']|float(0)}}
    {% else %}
      {{ states('sensor.solar_prod') }}
    {% endif %}

- name: "Battery Charge Ah"
  unique_id: "studer.batt_charge"
  unit_of_measurement: "Ah"
  state_class: "total_increasing"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'energy')['batteryCharge']|float(0)}}
    {% else %}
      {{ states('sensor.batt_charge') }}
    {% endif %}

- name: "Battery Discharge Ah"
  unique_id: "studer.batt_discharge"
  unit_of_measurement: "Ah"
  state_class: "total_increasing"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'energy')['batteryDischarge']|float(0)}}
    {% else %}
      {{ states('sensor.batt_discharge') }}
    {% endif %}

- name: "Battery Power In"
  unit_of_measurement: "kW"
  device_class: "power"
  state_class: "measurement"
  state: >
    {% set power = states('sensor.battpower' )|float(0) %}
    {% if power >= 0 %}
      {{ power }}
    {% else %}
      0
    {% endif %}

- name: "Battery Power Out"
  unit_of_measurement: "kW"
  device_class: "power"
  state_class: "measurement"
  state: >
    {% set power = states('sensor.battpower' )|float(0) %}
    {% if power < 0 %}
      {{ power|abs }}
    {% else %}
      0
    {% endif %}

- name: "Battery SOC Percent"
  unique_id: "studer.battsoc"
  unit_of_measurement: "%"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'battery')['soc']|float(0)}}
    {% else %}
      {{ states('sensor.battsoc') }}
    {% endif %}

- name: "Battery Power"
  unique_id: "studer.battpower"
  unit_of_measurement: "kW"
  device_class: "power"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'battery')['power']|float(0)}}
    {% else %}
      {{ states('sensor.battpower') }}
    {% endif %}

- name: "Battery Temperature"
  unique_id: "studer.batttemp"
  unit_of_measurement: "°C"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'battery')['temperature']|float(0)}}
    {% else %}
      {{ states('sensor.batttemp') }}
    {% endif %}

- name: "Battery Voltage"
  unique_id: "studer.battvoltage"
  unit_of_measurement: "V"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'battery')['voltage']|float(0)}}
    {% else %}
      {{ states('sensor.battvoltage') }}
    {% endif %}

- name: "PV Generation"
  unique_id: "studer.solarpower"
  unit_of_measurement: "kW"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'power')['solar']|float(0)}}
    {% else %}
      {{ states('sensor.solarpower') }}
    {% endif %}

- name: "Grid Consumption"
  unique_id: "studer.gridpower"
  unit_of_measurement: "kW"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'power')['gridGenset']|float(0)}}
    {% else %}
      {{ states('sensor.gridpower') }}
    {% endif %}

- name: "House Consumption"
  unique_id: "studer.houseload"
  unit_of_measurement: "kW"
  state: >-
    {% if state_attr('sensor.studer_synoptic', 'energy')['hasInverter'] == true %}
      {{state_attr('sensor.studer_synoptic', 'power')['load']|float(0)}}
    {% else %}
      {{ states('sensor.houseload') }}
    {% endif %}


# This block tests if the Studer portal responded with individual point data.
- name: "Studer AUX1 API Read Status"
  unique_id: "studer_aux1_api_read_status"
  state: >-
    {% if state_attr('sensor.studer_xtender_aux1', 'status') == "OK" %}
      {{'AUX Read OK'}}
    {% else %}
      {{'AUX Read FAIL'}}
    {% endif %}

#This block reads the data attribute from the RESTful sensor and saves it to another sensor if the last read was successful, if not, previous value is retained.
- name: "Studer AUX1"
  unique_id: "studer_aux1"
  state: >-
    {% if state_attr('sensor.studer_xtender_aux1', 'status') == "OK" %}
      {% if state_attr('sensor.studer_xtender_aux1', 'floatValue')| float(0) == 0 %}
        {{"opened"}}
      {% else %}
        {{"closed"}}  
      {% endif %}     
    {% else %}
      {{ states('sensor.studer_aux1') }}
    {% endif %}

- name: "Studer Electronic Temperature API Read Status"
  unique_id: "studer_electronic_temperature_api_read_status"
  state: >-
    {% if state_attr('sensor.studer_xtender_electronic_temperature', 'status') == "OK" %}
      {{'Electronic Temperature Read OK'}}
    {% else %}
      {{'Electronic Temperature Read FAIL'}}
    {% endif %}

- name: "Studer Electronic Temperature"
  unique_id: "studer_electronic_temperature"
  unit_of_measurement: "°C"
  device_class: "temperature"
  state_class: "measurement"
  state: >-
    {% if state_attr('sensor.studer_xtender_electronic_temperature', 'status') == "OK" %}
      {{ state_attr('sensor.studer_xtender_electronic_temperature', 'floatValue')| float(0) }}
    {% else %}
      {{ states('sensor.studer_electronic_temperature') }}
    {% endif %}

- name: "Studer AC Input Voltage API Read Status"
  unique_id: "studer_ac_input_voltage_api_read_status"
  state: >-
    {% if state_attr('sensor.studer_xtender_ac_input_voltage', 'status') == "OK" %}
      {{'AC Input Voltage Read OK'}}
    {% else %}
      {{'AC Input Voltage Read FAIL'}}
    {% endif %}

- name: "Studer AC Input Voltage"
  unique_id: "studer_ac_input_voltage"
  unit_of_measurement: "V"
  device_class: "voltage"
  state_class: "measurement"
  state: >-
    {% if state_attr('sensor.studer_xtender_ac_input_voltage', 'status') == "OK" %}
      {{ state_attr('sensor.studer_xtender_ac_input_voltage', 'floatValue') | float(0) | round(0) }}
    {% else %}
      {{ states('sensor.studer_ac_input_voltage') }}
    {% endif %}

- name: "Studer Battery Voltage API Read Status"
  unique_id: "studer_battery_voltage_api_read_status"
  state: >-
    {% if state_attr('sensor.studer_xtender_battery_voltage', 'status') == "OK" %}
      {{'Battery Voltage Read OK'}}
    {% else %}
      {{'Battery Voltage Read FAIL'}}
    {% endif %}

- name: "Studer Battery Voltage"
  unique_id: "studer_battery_voltage"
  unit_of_measurement: "V"
  device_class: "voltage"
  state_class: "measurement"
  state: >-
    {% if state_attr('sensor.studer_xtender_battery_voltage', 'status') == "OK" %}
      {{ state_attr('sensor.studer_xtender_battery_voltage', 'floatValue')| float(0) }}
    {% else %}
      {{ states('sensor.studer_battery_voltage') }}
    {% endif %}
1 Like

Conditional card to warn when portal isn’t responding with data so that you know the data you are seeing isn’t being refreshed:

type: conditional
conditions:
  - entity: sensor.studer_cloud_status
    state: Studer Cloud OFFLINE
card:
  type: entities
  entities:
    - entity: sensor.studer_cloud_status
      secondary_info: last-changed
  state_color: true

Studer dasboard gages:
image

1 Like

NEW 2023-10-07

An automation calls each of the RESTful integrations one at a time. By looking at graphs from the Synoptic data with the scan rate set very low, I saw that they were only updating every 13 seconds, so I set the scan rate in the automation to the slightly slower 15 seconds to avoid overrunning the Studer portal.

The automation also checks and increments a counter which is used to select which RESTful block is called at any given scan. The counter goes from 0 to 19, so the total time to complete a cycle is 300 seconds, or 5 minutes. The Synoptic data is what I am most interested in, so it is updated most often and is called 4 times in a row every 15 seconds, followed by a call to one of the individual data points.

Automation yaml code:

alias: Studer RESTful call
description: ""
trigger:
  - platform: time_pattern
    id: scan
    seconds: /15
condition: []
action:
  - choose:
      - conditions:
          - condition: or
            conditions:
              - condition: numeric_state
                entity_id: counter.studer_restful_scanner
                above: 0
                below: 5
              - condition: numeric_state
                entity_id: counter.studer_restful_scanner
                above: 5
                below: 10
              - condition: numeric_state
                entity_id: counter.studer_restful_scanner
                above: 10
                below: 15
              - condition: numeric_state
                entity_id: counter.studer_restful_scanner
                above: 15
                below: 20
        sequence:
          - service: homeassistant.update_entity
            data: {}
            target:
              entity_id:
                - sensor.studer_synoptic
      - conditions:
          - condition: numeric_state
            entity_id: counter.studer_restful_scanner
            below: 1
        sequence:
          - service: homeassistant.update_entity
            data: {}
            target:
              entity_id: sensor.studer_xtender_aux1
      - conditions:
          - condition: numeric_state
            entity_id: counter.studer_restful_scanner
            above: 4
            below: 6
        sequence:
          - service: homeassistant.update_entity
            data: {}
            target:
              entity_id: sensor.studer_xtender_electronic_temperature
      - conditions:
          - condition: numeric_state
            entity_id: counter.studer_restful_scanner
            above: 9
            below: 11
        sequence:
          - service: homeassistant.update_entity
            data: {}
            target:
              entity_id: sensor.studer_xtender_ac_input_voltage
      - conditions:
          - condition: numeric_state
            entity_id: counter.studer_restful_scanner
            above: 14
            below: 16
        sequence:
          - service: homeassistant.update_entity
            data: {}
            target:
              entity_id: sensor.studer_xtender_battery_voltage
  - if:
      - condition: numeric_state
        entity_id: counter.studer_restful_scanner
        below: 19
    then:
      - service: counter.increment
        data: {}
        target:
          entity_id: counter.studer_restful_scanner
    else:
      - service: counter.reset
        data: {}
        target:
          entity_id: counter.studer_restful_scanner
mode: single

Screens shot Counter used to scan RESTful API calls (counter was created using Helpers)

1 Like

Based on this excellent work by @JeffersM, I’ve got a first version of a proper HA integration for Studer-Innotec (via local LAN network connection using Xcom protocol) available for testing.
This no longer depends on the Studer web portal, which seems to be a bit unreliable and often results in ‘unknown’ sensor values.

It supports many Studer PV components, including:

  • Xtender XTH, XTS and XTM,
  • VarioTrack and VarioString
  • BSP and BMS
  • RCC

This integration would suit those Home Assistant users that do not like to be forced to configure their system via yaml files and scripts, but like to use a configuration wizard instead.

Use the configuration steps to choose which Studer infos should be shown as individual sensors (read-only). Likewise, choose which Studer params shoudld be shown as a writable entity (number, switch, select).

Am awaiting for it to be added to the HACS integrations. Meanwhile you can add it manually via the steps descrived in “https://github.com/ankohanse/hass-studer-xcom”

1 Like