This repository has been archived by the owner on Oct 23, 2022. It is now read-only.
forked from binsentsu/home-assistant-solaredge-modbus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add modbus address selection binsentsu#59
Add modbus address selection binsentsu#59
- Loading branch information
1 parent
05ea23e
commit e1f6b05
Showing
10 changed files
with
1,301 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import ipaddress | ||
import re | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries | ||
from homeassistant.const import CONF_NAME, CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL | ||
from .const import ( | ||
DOMAIN, | ||
DEFAULT_NAME, | ||
DEFAULT_SCAN_INTERVAL, | ||
DEFAULT_PORT, | ||
DEFAULT_MODBUS_ADDRESS, | ||
CONF_MODBUS_ADDRESS, | ||
CONF_NUMBER_INVERTERS, | ||
DEFAULT_NUMBER_INVERTERS, | ||
CONF_READ_METER1, | ||
CONF_READ_METER2, | ||
CONF_READ_METER3, | ||
DEFAULT_READ_METER1, | ||
DEFAULT_READ_METER2, | ||
DEFAULT_READ_METER3, | ||
) | ||
from homeassistant.core import HomeAssistant, callback | ||
|
||
DATA_SCHEMA = vol.Schema( | ||
{ | ||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str, | ||
vol.Required(CONF_HOST): str, | ||
vol.Required(CONF_PORT, default=DEFAULT_PORT): int, | ||
vol.Optional(CONF_MODBUS_ADDRESS, default=DEFAULT_MODBUS_ADDRESS): int, | ||
# Would like to restrict this to only allow numbers between 1-9 but not sure how | ||
vol.Required(CONF_NUMBER_INVERTERS, default=DEFAULT_NUMBER_INVERTERS): int, | ||
vol.Optional(CONF_READ_METER1, default=DEFAULT_READ_METER1): bool, | ||
vol.Optional(CONF_READ_METER2, default=DEFAULT_READ_METER2): bool, | ||
vol.Optional(CONF_READ_METER3, default=DEFAULT_READ_METER3): bool, | ||
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): int, | ||
} | ||
) | ||
|
||
|
||
def host_valid(host): | ||
"""Return True if hostname or IP address is valid.""" | ||
try: | ||
if ipaddress.ip_address(host).version == (4 or 6): | ||
return True | ||
except ValueError: | ||
disallowed = re.compile(r"[^a-zA-Z\d\-]") | ||
return all(x and not disallowed.search(x) for x in host.split(".")) | ||
|
||
|
||
@callback | ||
def solaredge_modbus_entries(hass: HomeAssistant): | ||
"""Return the hosts already configured.""" | ||
return set( | ||
entry.data[CONF_HOST] for entry in hass.config_entries.async_entries(DOMAIN) | ||
) | ||
|
||
|
||
class SolaredgeModbusConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Solaredge Modbus configflow.""" | ||
|
||
VERSION = 1 | ||
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL | ||
|
||
def _host_in_configuration_exists(self, host) -> bool: | ||
"""Return True if host exists in configuration.""" | ||
if host in solaredge_modbus_entries(self.hass): | ||
return True | ||
return False | ||
|
||
async def async_step_user(self, user_input=None): | ||
"""Handle the initial step.""" | ||
errors = {} | ||
|
||
if user_input is not None: | ||
host = user_input[CONF_HOST] | ||
|
||
if self._host_in_configuration_exists(host): | ||
errors[CONF_HOST] = "already_configured" | ||
elif not host_valid(user_input[CONF_HOST]): | ||
errors[CONF_HOST] = "invalid host IP" | ||
else: | ||
await self.async_set_unique_id(user_input[CONF_HOST]) | ||
self._abort_if_unique_id_configured() | ||
return self.async_create_entry( | ||
title=user_input[CONF_NAME], data=user_input | ||
) | ||
|
||
return self.async_show_form( | ||
step_id="user", data_schema=DATA_SCHEMA, errors=errors | ||
) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"domain": "solaredge_modbus", | ||
"name": "SolarEdge Modbus", | ||
"documentation": "https://github.com/binsentsu/home-assistant-solaredge-modbus", | ||
"requirements": ["pymodbus==1.5.2"], | ||
"dependencies": [], | ||
"codeowners": ["@binsentsu"], | ||
"config_flow": true, | ||
"version": "1.3.1" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import logging | ||
from typing import Optional, Dict, Any | ||
from .const import ( | ||
SENSOR_TYPES, | ||
METER1_SENSOR_TYPES, | ||
METER2_SENSOR_TYPES, | ||
METER3_SENSOR_TYPES, | ||
DOMAIN, | ||
ATTR_STATUS_DESCRIPTION, | ||
DEVICE_STATUSSES, | ||
ATTR_MANUFACTURER, | ||
) | ||
from datetime import datetime | ||
from homeassistant.helpers.entity import Entity | ||
from homeassistant.const import CONF_NAME, DEVICE_CLASS_ENERGY, ENERGY_KILO_WATT_HOUR | ||
from homeassistant.components.sensor import ( | ||
PLATFORM_SCHEMA, | ||
STATE_CLASS_MEASUREMENT, | ||
SensorEntity, | ||
) | ||
|
||
try: # backward-compatibility to 2021.8 | ||
from homeassistant.components.sensor import STATE_CLASS_TOTAL_INCREASING | ||
except ImportError: | ||
from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT as STATE_CLASS_TOTAL_INCREASING | ||
|
||
|
||
from homeassistant.core import callback | ||
from homeassistant.util import dt as dt_util | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
async def async_setup_entry(hass, entry, async_add_entities): | ||
hub_name = entry.data[CONF_NAME] | ||
hub = hass.data[DOMAIN][hub_name]["hub"] | ||
|
||
device_info = { | ||
"identifiers": {(DOMAIN, hub_name)}, | ||
"name": hub_name, | ||
"manufacturer": ATTR_MANUFACTURER, | ||
} | ||
|
||
entities = [] | ||
|
||
for inverter_index in range(hub.number_of_inverters): | ||
inverter_variable_prefix = "i" + str(inverter_index + 1) + "_" | ||
inverter_title_prefix = "I" + str(inverter_index + 1) + " " | ||
for sensor_info in SENSOR_TYPES.values(): | ||
sensor = SolarEdgeSensor( | ||
hub_name, | ||
hub, | ||
device_info, | ||
inverter_title_prefix + sensor_info[0], | ||
inverter_variable_prefix + sensor_info[1], | ||
sensor_info[2], | ||
sensor_info[3], | ||
) | ||
entities.append(sensor) | ||
|
||
if hub.read_meter1 == True: | ||
for meter_sensor_info in METER1_SENSOR_TYPES.values(): | ||
sensor = SolarEdgeSensor( | ||
hub_name, | ||
hub, | ||
device_info, | ||
meter_sensor_info[0], | ||
meter_sensor_info[1], | ||
meter_sensor_info[2], | ||
meter_sensor_info[3], | ||
) | ||
entities.append(sensor) | ||
|
||
if hub.read_meter2 == True: | ||
for meter_sensor_info in METER2_SENSOR_TYPES.values(): | ||
sensor = SolarEdgeSensor( | ||
hub_name, | ||
hub, | ||
device_info, | ||
meter_sensor_info[0], | ||
meter_sensor_info[1], | ||
meter_sensor_info[2], | ||
meter_sensor_info[3], | ||
) | ||
entities.append(sensor) | ||
|
||
if hub.read_meter3 == True: | ||
for meter_sensor_info in METER3_SENSOR_TYPES.values(): | ||
sensor = SolarEdgeSensor( | ||
hub_name, | ||
hub, | ||
device_info, | ||
meter_sensor_info[0], | ||
meter_sensor_info[1], | ||
meter_sensor_info[2], | ||
meter_sensor_info[3], | ||
) | ||
entities.append(sensor) | ||
|
||
async_add_entities(entities) | ||
return True | ||
|
||
|
||
class SolarEdgeSensor(SensorEntity): | ||
"""Representation of an SolarEdge Modbus sensor.""" | ||
|
||
def __init__(self, platform_name, hub, device_info, name, key, unit, icon): | ||
"""Initialize the sensor.""" | ||
self._platform_name = platform_name | ||
self._hub = hub | ||
self._key = key | ||
self._name = name | ||
self._unit_of_measurement = unit | ||
self._icon = icon | ||
self._device_info = device_info | ||
self._attr_state_class = STATE_CLASS_MEASUREMENT | ||
if self._unit_of_measurement == ENERGY_KILO_WATT_HOUR: | ||
self._attr_state_class = STATE_CLASS_TOTAL_INCREASING | ||
self._attr_device_class = DEVICE_CLASS_ENERGY | ||
if STATE_CLASS_TOTAL_INCREASING == STATE_CLASS_MEASUREMENT: # compatibility to 2021.8 | ||
self._attr_last_reset = dt_util.utc_from_timestamp(0) | ||
|
||
async def async_added_to_hass(self): | ||
"""Register callbacks.""" | ||
self._hub.async_add_solaredge_sensor(self._modbus_data_updated) | ||
|
||
async def async_will_remove_from_hass(self) -> None: | ||
self._hub.async_remove_solaredge_sensor(self._modbus_data_updated) | ||
|
||
@callback | ||
def _modbus_data_updated(self): | ||
self.async_write_ha_state() | ||
|
||
@callback | ||
def _update_state(self): | ||
if self._key in self._hub.data: | ||
self._state = self._hub.data[self._key] | ||
|
||
@property | ||
def name(self): | ||
"""Return the name.""" | ||
return f"{self._platform_name} ({self._name})" | ||
|
||
@property | ||
def unique_id(self) -> Optional[str]: | ||
return f"{self._platform_name}_{self._key}" | ||
|
||
@property | ||
def unit_of_measurement(self): | ||
"""Return the unit of measurement.""" | ||
return self._unit_of_measurement | ||
|
||
@property | ||
def icon(self): | ||
"""Return the sensor icon.""" | ||
return self._icon | ||
|
||
@property | ||
def state(self): | ||
"""Return the state of the sensor.""" | ||
if self._key in self._hub.data: | ||
return self._hub.data[self._key] | ||
|
||
@property | ||
def extra_state_attributes(self): | ||
if self._key in ["status", "statusvendor"]: | ||
if self.state in DEVICE_STATUSSES: | ||
return {ATTR_STATUS_DESCRIPTION: DEVICE_STATUSSES[self.state]} | ||
return None | ||
|
||
@property | ||
def should_poll(self) -> bool: | ||
"""Data is delivered by the hub""" | ||
return False | ||
|
||
@property | ||
def device_info(self) -> Optional[Dict[str, Any]]: | ||
return self._device_info |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"config": { | ||
"title": "SolarEdge Modbus", | ||
"step": { | ||
"user": { | ||
"title": "Define your SolarEdge modbus connection", | ||
"data": { | ||
"host": "The ip-address of your Solaredge device", | ||
"name": "The prefix to be used for your SolarEdge sensors", | ||
"port": "The TCP port on which to connect to the SolarEdge", | ||
"modbus_address": "The starting modbus address", | ||
"number_of_inverters": "The number of inverters connected", | ||
"read_meter_1": "Read meter 1 data (only for meter models)", | ||
"read_meter_2": "Read meter 2 data (only for meter models)", | ||
"read_meter_3": "Read meter 3 data (only for meter models)", | ||
"scan_interval": "The polling frequency of the modbus registers in seconds" | ||
} | ||
} | ||
}, | ||
"error": { | ||
"already_configured": "Device is already configured" | ||
}, | ||
"abort": { | ||
"already_configured": "Device is already configured" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"config": { | ||
"title": "SolarEdge Modbus", | ||
"step": { | ||
"user": { | ||
"title": "Konfiguriere Deine modbus-Verbindung zum SolarEdge Wechselrichter.", | ||
"data": { | ||
"host": "Die IP-Adresse des Solaredge Wechselrichters", | ||
"name": "Das Prefix, das für die SolarEdge Sensoren verwendet werden soll", | ||
"port": "Der TCP Port des SolarEdge Wechselrichters (z.B. 502)", | ||
"number_of_inverters": "Die Anzahl der angeschlossenen Wechselrichter", | ||
"read_meter_1": "Lese die Stände des Zähler 1 (nur für Modelle mit Zähler)", | ||
"read_meter_2": "Lese die Stände des Zähler 2 (nur für Modelle mit Zähler)", | ||
"read_meter_3": "Lese die Stände des Zähler 3 (nur für Modelle mit Zähler)", | ||
"scan_interval": "Das Abfrageintervall der modbus Register in Sekunden" | ||
} | ||
} | ||
}, | ||
"error": { | ||
"already_configured": "Der Wechselrichter ist bereits konfiguriert.," | ||
}, | ||
"abort": { | ||
"already_configured": "Der Wechselrichter ist bereits konfiguriert." | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"config": { | ||
"title": "SolarEdge Modbus", | ||
"step": { | ||
"user": { | ||
"title": "Define your SolarEdge modbus connection", | ||
"data": { | ||
"host": "The ip-address of your Solaredge inverter", | ||
"name": "The prefix to be used for your SolarEdge sensors", | ||
"port": "The TCP port on which to connect to the SolarEdge inverter", | ||
"number_of_inverters": "The number of inverters connected", | ||
"read_meter_1": "Read meter 1 data (only for meter models)", | ||
"read_meter_2": "Read meter 2 data (only for meter models)", | ||
"read_meter_3": "Read meter 3 data (only for meter models)", | ||
"scan_interval": "The polling frequency of the modbus registers in seconds" | ||
} | ||
} | ||
}, | ||
"error": { | ||
"already_configured": "Device is already configured" | ||
}, | ||
"abort": { | ||
"already_configured": "Device is already configured" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"config": { | ||
"title": "SolarEdge Modbus", | ||
"step": { | ||
"user": { | ||
"title": "Definer din SolarEdge modbus-tilkobling", | ||
"data": { | ||
"host": "IP-adressen til din Solaredge-omformer", | ||
"name": "Prefikset som skal brukes til SolarEdge-sensorene dine", | ||
"port": "TCP-porten som skal kobles til SolarEdge-omformeren", | ||
"number_of_inverters": "Antall omformere koblet sammen", | ||
"read_meter_1": "Les måler 1-data (bare for målermodeller)", | ||
"read_meter_2": "Les måler 2-data (bare for målermodeller)", | ||
"read_meter_3": "Les måler 3-data (bare for målermodeller)", | ||
"scan_interval": "Avstemningsfrekvensen til modbus registreres på få sekunder" | ||
} | ||
} | ||
}, | ||
"error": { | ||
"already_configured": "Enheten er allerede konfigurert" | ||
}, | ||
"abort": { | ||
"already_configured": "Enheten er allerede konfigurert" | ||
} | ||
} | ||
} |
Oops, something went wrong.