Having struggled with the default Solax integration, I’ve set myself a challenge of using the available local and cloud APIs instead. The main motivation behind this was to get real-time data from the inverter, particularly the grid in/out readings from the CT clamp so that I can maximise self-use. In addition, being able to charge the battery from the grid in case of low solar forecast would be a nice to have.
The end result looks like this:
… plus a few automations which I will cover later.
My setup is as follows:
- Solax X1-Hybrid-G4 with Solax Pocket Wifi (X1-Hybrid-5.0-D; type=15; version=3.003.02)
- Two Solax Triple Power HV10230V2 3.1kWh batteries
- 6.48kW of solar panels (JA)
- Container-based Home Assistant with the following integrations:
- HACS
- PowerCalc
- Power Flow Card
- Forecast Solar
All the configuration has been split into the following files:
- configuration.yaml
- sensor.yaml (where the REST connectors are)
- secrets.yaml (for the API keys and payloads)
- powercalc.yaml (for the dashboards)
- automations.yaml
Cloud APIs
The integration with the Solax Cloud API was straightforward thanks to @ColinRobbins and his great article. While this should be sufficient for most people, I was really keen on the real-time monitoring aspect. What is more, the cloud API is only refreshed every 5min and occasionally doesn’t work at all, so not having a hard dependency on Solax Cloud for basic home monitoring and automation was also beneficial.
Local APIs
Most importantly, the local connection does not need any extra configuration and can be polled as often as requred (in my case every 5 seconds). As if by magic, I can access my inverter on a local IP address (192.168.1.x) and issue the same commands as being on the Solax Pocket Wifi and using the 5.8.8.8 address to connect! Here is the curl command:
curl -d "optType=ReadRealTimeData&pwd=REG_NO" -X POST http://192.168.1.x
where REG_NO
is the Solax Registration Number (e.g. SV2AMZFWPT).
I’ve based the local connection configuration on the following resources:
Most of the configuration has been copied from the referenced resources, but in order to distinguish between the cloud and local connections, all the cloud sensors have a solax_cloud
prefix, and all the local ones solax_local
.
In addition to reading the standard inverter data, I’ve managed to pull the inverter settings and update one of the self-use configurations. Here are the decoded fields for both real-time data and settings (read set data): X1 Hybrid G4 - Google Sheets - any help to decode more will be appreciated of course!
Configuration files
Below are complete snippets you can adopt to your needs - for all my setup please see my github repo.
secrets.yaml
solax_cloud_api: https://www.solaxcloud.com/proxyApp/proxy/api/getRealtimeInfo.do?tokenId=TOKEN&sn=REG_NO
solax_local_ip: http://192.168.1.x/ # IP of solax module
solax_local_realtime_payload: "optType=ReadRealTimeData&pwd=REG_NO"
solax_local_settings_payload: "optType=ReadSetData&pwd=REG_NO"
solax_local_set_battery_min_soc_payload: 'optType=setReg&pwd=REG_NO&data={"num":1,"Data":[{"reg":29,"val":"{{ level }}"}]}'
solax_local_set_battery_grid_charge_level_payload: 'optType=setReg&pwd=REG_NO&data={"num":1,"Data":[{"reg":31,"val":"{{ level }}"}]}'
solax_local_set_battery_forced_charge_start: 'optType=setReg&pwd=REG_NO&data={"num":1,"Data":[{"reg":37,"val":"{{ value }}"}]}'
solax_local_set_period2_enable_payload: 'optType=setReg&pwd=REG_NO&data={"num":1,"Data":[{"reg":41,"val":"{{ enabled }}"}]}'
You can obtain the tokenId
and the registration number (REG_NO
) from Solax Cloud.
sensor.yaml
###################################
# Solax X1 Hybrid G4 Rest Sensors #
###################################
# Solax Cloud REST
- platform: rest
scan_interval: 595
resource: !secret solax_cloud_api
json_attributes_path: "$.result"
json_attributes:
- yieldtoday
- yieldtotal
- acpower
- uploadTime
- inverterStatus
- feedinpower
- feedinenergy
- consumeenergy
- soc
- batPower
- powerdc1
- powerdc2
- batStatus
value_template: 'Active' # dummy value, not used; avoids the "State max length is 255 characters" error
name: "solax_rest_cloud"
### Solax Local REST sensor ###
- platform: rest
scan_interval: 5
resource: !secret solax_local_ip
payload: !secret solax_local_realtime_payload
method: POST
name: "solax_rest_local"
json_attributes:
- sn
- ver
- type
- Data
- Information
value_template: 'OK' # dummy value, not used; avoids the "State max length is 255 characters" error
### Solax Local REST settings ###
- platform: rest
scan_interval: 3600
resource: !secret solax_local_ip
payload: !secret solax_local_settings_payload
method: POST
name: "solax_rest_local_settings"
# Unfortunately settings are not returned as a JSON document but an array of numbers, so having to pick just the relevant to avoid the max 255 chars limit
# 0 - Self Use Min SOC %
# 1 - Self Use Charge from grid (0 for disabled, 1 for enabled)
# 2 - Self Use Charge from grid to %
value_template: "{{ '[' ~ value.split(',')[28] ~ ',' ~ value.split(',')[29] ~ ',' ~ value.split(',')[30] ~ ']' }}"
configuration.yaml
# Loads default set of integrations. Do not remove.
default_config:
# Load frontend themes from the themes folder
frontend:
themes: !include_dir_merge_named themes
powercalc:
logger:
default: info
logs:
homeassistant.components.rest: info
# custom_components.powercalc: debug
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
template:
- sensor:
- name: "Solax Cloud Yield Today"
state: "{{ state_attr('sensor.solax_rest_cloud', 'yieldtoday') }}"
unit_of_measurement: "kWh"
unique_id: solax_2
icon: mdi:flash
device_class: energy
state_class: total_increasing
- name: "Solax Cloud Yield Total"
state: "{{ state_attr('sensor.solax_rest_cloud', 'yieldtotal') }}"
unit_of_measurement: "kWh"
unique_id: solax_3
icon: mdi:flash
device_class: energy
state_class: total_increasing
- name: "Solax Cloud Inverter"
unit_of_measurement: "W"
state: "{{ state_attr('sensor.solax_rest_cloud', 'acpower') }}"
unique_id: solax_cloud_inverter
icon: mdi:flash
device_class: energy
state_class: measurement
- name: "Solax Cloud Upload Time"
state: >
{% set time = state_attr('sensor.solax_rest_cloud', 'uploadTime') %}
{{ as_timestamp(time) | timestamp_custom('%I:%M %p') }}
unique_id: solax_5
icon: mdi:clock
- name: "Solax Cloud Battery Status"
state: "{{ state_attr('sensor.solax_rest_cloud', 'batStatus') }}"
unique_id: solax_6
icon: mdi:battery
- name: "Solax Cloud Solar Panel"
state: "{{ state_attr('sensor.solax_rest_cloud', 'powerdc1') + state_attr('sensor.solax_rest_cloud', 'powerdc2') }}"
unit_of_measurement: "W"
device_class: energy
state_class: measurement
unique_id: solax_cloud_solar_panel
icon: mdi:solar-power-variant
- name: "Solax Cloud Battery Use"
state: >
{{ state_attr('sensor.solax_rest_cloud', 'batPower') }}
unit_of_measurement: "W"
device_class: energy
state_class: measurement
unique_id: solax_cloud_battery_use
icon: mdi:battery
- name: "Solax Cloud Battery Adjusted"
state: >
{% set ac = states('sensor.solax_cloud_inverter')|int %}
{% set pv = states('sensor.solax_cloud_solar_panel')|int %}
{{ (0 - ac + pv) }}
unit_of_measurement: "W"
device_class: energy
state_class: measurement
unique_id: solax_cloud_battery_adjusted
icon: mdi:battery
- name: "Solax Cloud Battery Use In"
state: >
{% set batAdj = states('sensor.solax_cloud_battery_adjusted')|int(default=0) %}
{% set attr = state_attr('sensor.solax_rest_cloud', 'batPower') %}
{{ batAdj if is_number(batAdj) and (batAdj|int > 0) else 0 }}
unit_of_measurement: "W"
unique_id: solax_cloud_battery_use_in
icon: mdi:battery
- name: "Solax Cloud Battery Use Out"
state: >
{% set batAdj = states('sensor.solax_cloud_battery_adjusted')|int(default=0) %}
{% set attr = state_attr('sensor.solax_rest_cloud', 'batPower') %}
{{ (0 - batAdj) if is_number(batAdj) and (batAdj|int < 0) else 0 }}
unit_of_measurement: "W"
unique_id: solax_cloud_battery_use_out
icon: mdi:battery
- name: "Solax Cloud Battery"
state: "{{ state_attr('sensor.solax_rest_cloud', 'soc') }}"
unit_of_measurement: "%"
device_class: battery
state_class: measurement
unique_id: solax_12
icon: mdi:battery
- name: "Solax Cloud Grid Power"
state: >
{% set attr = state_attr('sensor.solax_rest_cloud', 'feedinpower') %}
{{ (0 - attr) if is_number(attr) else 0 }}
unit_of_measurement: "W"
device_class: energy
state_class: measurement
unique_id: solax_13
icon: mdi:transmission-tower
- name: "Solax Cloud Grid Power in"
state: >
{% set attr = state_attr('sensor.solax_rest_cloud', 'feedinpower') %}
{{ (0 - attr) if is_number(attr) and (attr|float < 0) else 0 }}
unit_of_measurement: "W"
unique_id: solax_14
icon: mdi:transmission-tower
- name: "Solax Cloud Grid Power out"
state: >
{% set attr = state_attr('sensor.solax_rest_cloud', 'feedinpower') %}
{{ attr if is_number(attr) and (attr|float > 0) else 0 }}
unit_of_measurement: "W"
unique_id: solax_15
icon: mdi:transmission-tower
- name: "Solax Cloud Energy To Grid"
state: "{{ state_attr('sensor.solax_rest_cloud', 'feedinenergy') }}"
unit_of_measurement: "kWh"
unique_id: solax_16
icon: mdi:transmission-tower
device_class: energy
state_class: total_increasing
- name: "Solax Cloud Energy From Grid"
state: "{{ state_attr('sensor.solax_rest_cloud', 'consumeenergy') }}"
unit_of_measurement: "kWh"
unique_id: solax_19
icon: mdi:transmission-tower
device_class: energy
state_class: total_increasing
- name: "Solax Cloud Status"
unique_id: solax_20
icon: mdi:solar-power-variant
state: >
{% set attr = state_attr('sensor.solax_rest_cloud', 'inverterStatus') %}
{% if attr == '100' %}Wait
{% elif attr == '101' %}Check
{% elif attr == '102' %}Normal
{% elif attr == '103' %}Fault
{% elif attr == '104' %}Permanent Fault
{% elif attr == '105' %}Update
{% elif attr == '106' %}EPS Check
{% elif attr == '107' %}EPS
{% elif attr == '108' %}Self-test
{% elif attr == '109' %}Idle
{% elif attr == '110' %}Standby
{% elif attr == '111' %}Pv Wake Up Bat
{% elif attr == '112' %}Gen Check
{% elif attr == '113' %}Gen Run
{% else %}unknown{% endif %}
- name: SolaX Cloud Total Home Use
unique_id: home use
state: >
{% set battery = states('sensor.solax_battery_use') %}
{% set grid = states('sensor.solax_grid_power') %}
{% set solar = states('sensor.solar_panel_power') %}
{% set total = 0 - battery|float if is_number(battery) else 0 %}
{% set total = total + (solar|float if is_number(solar) else 0) %}
{% set total = total + (grid|float if is_number(grid) else 0) %}
{{ total }}
state_class: measurement
icon: 'mdi:flash'
unit_of_measurement: W
device_class: power
# --- LOCAL --------------------------
### Local sensor readings ###
# Each valid SN seems to be 10 characters
- name: solax_local
state: >
{% if state_attr('sensor.solax_rest_local', 'sn')|length == 10 %}
{{ now().strftime("%H:%M:%S") }}
{% else %}
{{ (states('sensor.solax_local')) }}
{% endif %}
attributes:
sn: >-
{% if state_attr('sensor.solax_rest_local', 'sn')|length == 10 %}
{{ (state_attr('sensor.solax_rest_local', 'sn')) }}
{% else %}
{{ (state_attr('sensor.solax_local', 'sn')) }}
{% endif %}
ver: >-
{% if state_attr('sensor.solax_rest_local', 'sn')|length == 10 %}
{{ (state_attr('sensor.solax_rest_local', 'ver')) }}
{% else %}
{{ (state_attr('sensor.solax_local', 'ver')) }}
{% endif %}
type: >-
{% if state_attr('sensor.solax_rest_local', 'sn')|length == 10 %}
{{ (state_attr('sensor.solax_rest_local', 'type')) }}
{% else %}
{{ (state_attr('sensor.solax_local', 'type')) }}
{% endif %}
Data: >-
{% if state_attr('sensor.solax_rest_local', 'sn')|length == 10 %}
{{ (state_attr('sensor.solax_rest_local', 'Data')) }}
{% else %}
{{ (state_attr('sensor.solax_local', 'Data')) }}
{% endif %}
Information: >-
{% if state_attr('sensor.solax_rest_local', 'sn')|length == 10 %}
{{ (state_attr('sensor.solax_rest_local', 'Information')) }}
{% else %}
{{ (state_attr('sensor.solax_local', 'Information')) }}
{% endif %}
### Make the settings look like sensor data by embedding the info in the Data attribute
- name: solax_local_settings
state: >
{{ now().strftime("%H:%M:%S") }}
attributes:
Data: >-
{{ (states('sensor.solax_rest_local_settings')) }}
#### Combined Solar PV output ####
- name: "Solax Local PV Output"
unique_id: solax_local_pv_output
state: "{{ (state_attr('sensor.solax_local', 'Data')[8] + state_attr('sensor.solax_local', 'Data')[9]) | int(default=0) }}"
unit_of_measurement: "W"
state_class: measurement
device_class: "power"
### Inverter output (negative for charging battery) ####
- name: "Solax Local AC Power"
unique_id: solax_local_ac_power
state: >
{% if state_attr('sensor.solax_local', 'Data')[2] > 32767 %}{{ (state_attr('sensor.solax_local', 'Data')[2] - 65536) | int(default=0) }}
{% else %}{{ state_attr('sensor.solax_local', 'Data')[2] | int(default=0) }}{% endif %}
unit_of_measurement: "W"
state_class: measurement
device_class: "power"
### Battery current, voltage and temperature ###
- name: "Solax Local Battery Voltage"
unique_id: solax_local_battery_voltage
state: "{{ state_attr('sensor.solax_local', 'Data')[14] | float / 100}}"
unit_of_measurement: "V"
device_class: "voltage"
- name: "Solax Local Battery Current"
unique_id: solax_local_battery_current
state: >
{% if state_attr('sensor.solax_local', 'Data')[15] > 32767 %}{{ (state_attr('sensor.solax_local', 'Data')[15] - 65536) / 100 }}
{% else %}{{ state_attr('sensor.solax_local', 'Data')[15] / 100 }}{% endif %}
unit_of_measurement: "A"
device_class: "current"
- name: "Solax Local Battery Temperature"
unique_id: solax_local_battery_temp
state: "{{ state_attr('sensor.solax_local', 'Data')[17] | int(default=0) }}"
unit_of_measurement: "°C"
device_class: "temperature"
### Battery charging/discharging power (positive for charging the battery) ###
- name: "Solax Local Battery Power"
unique_id: solax_local_battery_power
state: >
{% if state_attr('sensor.solax_local', 'Data')[16] > 32767 %}{{ state_attr('sensor.solax_local', 'Data')[16] - 65536 }}
{% else %}{{ state_attr('sensor.solax_local', 'Data')[16] }}{% endif %}
unit_of_measurement: "W"
#state_class: measurement
device_class: "power"
- name: "Solax Local Battery Power Adjusted"
unique_id: solax_local_battery_power_adjusted
state: >
{% set battery = states('sensor.solax_local_battery_power')|int %}
{% set ac = states('sensor.solax_local_ac_power')|int %}
{% set pv = states('sensor.solax_local_pv_output')|int %}
{{ (0 - ac + pv) }}
unit_of_measurement: "W"
#state_class: measurement
device_class: "power"
### Battery charge level (%)
- name: "Solax Local Battery SoC"
unique_id: solax_local_battery_soc
state: "{{ state_attr('sensor.solax_local', 'Data')[18] | int(default=0) }}"
unit_of_measurement: "%"
### Battery discharge min (%)
- name: "Solax Local Battery Min SoC"
unique_id: solax_local_battery_min_soc
state: "{{ state_attr('sensor.solax_local_settings', 'Data')[0] | int(default=0) }}"
unit_of_measurement: "%"
### Battery charge from grid
- name: "Solax Local Battery Charge From Grid"
unique_id: solax_local_battery_grid_enabled
state: "{{ state_attr('sensor.solax_local_settings', 'Data')[1] | int(default=0) }}"
### Battery charge level to (%) from grid
- name: "Solax Local Battery Charge From Grid To"
unique_id: solax_local_battery_charge_from_grid_to
state: "{{ state_attr('sensor.solax_local_settings', 'Data')[2] | int(default=0) }}"
unit_of_measurement: "%"
### Estimated remaining energy ###
- name: "Solax Local Battery Remain Energy"
unique_id: solax_local_battery_kwh
state: "{{ states('sensor.solax_local_battery_soc') | int(default=0) * 6.2 / 100 }}"
unit_of_measurement: "kWh"
device_class: "energy"
### Grid power (positive for feed-in, negative for consumption) ###
- name: "Solax Local Grid Power"
unique_id: solax_local_grid_power
state: >
{% if state_attr('sensor.solax_local', 'Data')[32] > 32767 %}{{ (state_attr('sensor.solax_local', 'Data')[32] - 65536) }}
{% else %}{{ state_attr('sensor.solax_local', 'Data')[32] }}{% endif %}
unit_of_measurement: "W"
state_class: measurement
device_class: "power"
### Expected household load (negative not expected as it only consumes energy) ###
- name: "Solax Local Load Power"
unique_id: solax_local_load_power
state: "{{ states('sensor.solax_local_ac_power')| float(default=0) - states('sensor.solax_local_grid_power') | int(default=0) }}"
unit_of_measurement: "W"
device_class: "power"
sensor: !include sensor.yaml
sensor powercalc_label: !include powercalc.yaml
### Commands for controlling the Solax Local inverter
rest_command:
solax_local_charge_battery_from_grid:
url: !secret solax_local_ip
method: post
payload: !secret solax_local_set_battery_level_payload
powercalc.yaml
- platform: powercalc
entity_id: sensor.solax_local_pv_output
name: Solar Panels V2
fixed:
power: "{{ states('sensor.solax_local_pv_output')| int(default=0) }}"
- platform: powercalc
entity_id: sensor.solax_cloud_solar_panel
name: Solax Cloud Solar Panels V1
fixed:
power: "{{ states('sensor.solax_cloud_solar_panel')| int(default=0) }}"
- platform: powercalc
entity_id: sensor.solax_cloud_battery_use_in
name: Solar Cloud Battery In V1
fixed:
power: "{{states('sensor.solax_cloud_battery_use_in')}}"
- platform: powercalc
entity_id: sensor.solax_cloud_battery_use_out
name: Solar Cloud Battery Out V1
fixed:
power: "{{states('sensor.solax_cloud_battery_use_out')}}"
Smart battery charging
The purpose of the below automation is to leverage cheap night-time electricity tariffs (e.g. Octopus Go) and avoid higher daytime prices. The charge targets are quite conservative in order to avoid hitting 100% charge during the day and having to send solar back to the grid - maximising self-use. Overall, the script wakes up at 23:30 every night and tries to set a new battery charge target (assuming charge from grid is enabled in the inverter settings). To calculate the appropriate value, we use tomorrow’s solar forecast. The new target is saved to an input number
so that it can be easily compared with the refreshed settings. This is attempted up to 3 times to make sure the settings reflect the newly set target.
To cater for sunny days when at the start of the off-peak tariff where is remaning energy from the previous day, the bottom two automations allow for discharge down to the newly calculated target. This is to use up all left-over electricity and get the system ready for the next day. Once the charge level drops to the new charge target, the automation kicks in to stop further discharge by setting the start period for forced charge to 04:25 (same as end of forced charge window).
With these three automations the battery is in use pretty much all the time - so in addition to just capturing surplus solar, it can also intelligently use off-peak tariffs and top up the battery for less sunny days.
automations.yaml
- id: '1395837280000'
alias: Set Solar Battery Target Level
description: Set target battery level (charge from grid) based on anticipated solar production
trigger:
- platform: time
at: "23:30:00"
condition: []
action:
- repeat:
sequence:
- service: input_number.set_value
data_template:
entity_id: input_number.new_battery_target
value: >
{% set solar = states('sensor.energy_production_tomorrow') %}
{% set target = 92 %}
{% if is_number(solar) %}
{% set target = 50 if (solar|float > 3 and solar|float <= 4) else target %}
{% set target = 30 if (solar|float > 4 and solar|float <= 5) else target %}
{% set target = 25 if (solar|float > 5 and solar|float <= 6) else target %}
{% set target = 20 if (solar|float > 7 and solar|float <= 8) else target %}
{% set target = 15 if (solar|float > 8) else target %}
{% endif %}
{{ target }}
- service: rest_command.solax_local_set_charge_battery_from_grid
data:
# Max level to 92% to reduce battery wear. Minimum to 15% (10% for the min level plus 5% for early morning use until the sun comes out).
level: >
{{ states('input_number.new_battery_target') }}
- delay:
hours: 0
minutes: 0
seconds: 10
milliseconds: 0
- service: homeassistant.update_entity
entity_id: sensor.solax_rest_local_settings
until:
- condition: template
# Try up to 3 times if the updated setting doen't reflect the target
value_template: >-
{{ states('sensor.solax_local_battery_charge_from_grid_to')|int == states('input_number.new_battery_target')|int or
repeat.index == 3 }}
mode: single
# Always reset to the same state - i.e. no off-peak forced charging overnight
- id: "1143938438939"
alias: Battery - Disable forced charge
description: "Allows for discharge if too much energy stored from previous day (default state)"
trigger:
- platform: time
at: "23:40:00"
- platform: time
at: "23:50:00"
action:
- repeat:
sequence:
- service: rest_command.solax_local_set_forced_charge_start
data:
value: >
{{ 4 + 25 * 256 }}
- delay:
hours: 0
minutes: 0
seconds: 15
milliseconds: 0
- service: homeassistant.update_entity
entity_id: sensor.solax_rest_local_settings
until:
- condition: template
# Try up to 3 times if the updated setting doen't reflect the target
value_template: >-
{{ states('sensor.solax_local_battery_setting_start_charge') == '04:25' or repeat.index == 3 }}
mode: single
# Detect if battery needs topping up within the off-peak tariff or if discharge should stop and the remainder power be left for peak usage
# (+4 is how much the inverter overshoots the target charge, +4 is buffer in case of fast discharge)
- id: "1029376657476"
alias: Battery - Enable forced charge
description: "Stops allowed discharge and allows for charging to target"
trigger:
- platform: time_pattern
minutes: "/1"
condition:
- condition: time
after: "00:25:00"
before: "04:20:00"
- condition: template
value_template: >-
{{ states('sensor.solax_local_battery_soc')|int <= states('sensor.solax_local_battery_charge_from_grid_to')|int + 8 }}
action:
- repeat:
sequence:
- service: rest_command.solax_local_set_forced_charge_start
data:
value: >
{{ 0 + 35 * 256 }}
- delay:
hours: 0
minutes: 0
seconds: 15
milliseconds: 0
- service: homeassistant.update_entity
entity_id: sensor.solax_rest_local_settings
until:
- condition: template
# Try up to 3 times if the updated setting doen't reflect the target
value_template: >-
{{ states('sensor.solax_local_battery_setting_start_charge') == '00:35' or repeat.index == 3 }}
mode: single
And here is what it looks like in practice (top - actual solar output, bottom - battery charge level in %):
Smart electric heater
This automation is about prioritising the battery charging from solar to almost full, while gradually using excess solar power and minimise grid export. This uses a dumb 2kW electric heater (on/off) and a smart plug rated to 13A. For details please see Solar-powered smart convector heater.
Smart immersion heating (hot water)
I’ve written a separate article covering hot water immersion heating. Not only for excess solar, but one that should provide a complete hot water heating solution (night-time, solar and evening top-up). Check it out at Smart immersion heating for domestic hot water.
Octopus Saving Sessions
For details on trying to automate forced export during the Octopus Saving Sessions please see Automated Octopus Saving Sessions with Solax X1 Hybrid G4 page.
Summary
I’m defenitely not an expect in Home Assistant, but I hope this tested configuration proves useful to a few people. While I’m confident with the monitoring aspects, I definitely have more testing and improvements to make on the control & automation side.
Any suggestions on what to improve will be highly appreciated. Happy monitoring!