Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues with sensors not updating after HA 2024.1 upgrade #37

Merged
merged 12 commits into from
Jan 16, 2024
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ In particular, if HA seems to not receive any data, a first step is to validate

## Changelog:

- 1.7 - 09/01/2023: Fixed issue [#36] sensors not updating after HA update to 2024.1. Updated sensor types to address deprecation warnings for combinarions of device and state classes [@shortbloke]
- 1.6 - 05/05/2022: ported Energy support to latest HA core releases [@shortbloke]
* 1.5 - 20/09/2021: added support for the Energy feature in HA, and included [@shortbloke] a templated sensor for the Grid consumption
* 1.4 - 05/05/2019: added resources.json and updated code layout following 'the great migration'
Expand Down
18 changes: 14 additions & 4 deletions custom_components/owlintuition/owl_intuition.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
####################################################
# Owl Intution - Grid Energy Sensor #
# Owl Intuition - Energy Sensor #
####################################################

template:
# State ensures:
# - value is not set until both solar and electricity readings have been received and are no longer unknown
# - reads the values in simple variables for elec and solar, to make if statements easier to read
# - protects value going negative if one sensor resets to zero before the other at around midnight
- trigger:
- platform: state
entity_id: sensor.owl_intuition_electricity_power
sensor:
- name: "Owl Grid Power Now"
- name: "Owl Grid Energy Now"
glpatcern marked this conversation as resolved.
Show resolved Hide resolved
glpatcern marked this conversation as resolved.
Show resolved Hide resolved
unit_of_measurement: "W"
state_class: measurement
device_class: power
Expand All @@ -23,6 +28,11 @@ template:
{% endif %}
{% endif %}

# State ensures:
# - value is not set until both solar and electricity readings have been received and are no longer unknown
# - reads the values in simple variables for elec, solar and last_grid_today, to make if statements easier to read
# - protects value going negative if one sensor resets to zero before the other at around midnight
# - if elec - solar would be negative it uses the last value from the sensor, stored in last_grid_today
- trigger:
- platform: state
entity_id: sensor.owl_intuition_electricity_today
Expand All @@ -37,12 +47,12 @@ template:
{% else %}
{% set elec = states('sensor.owl_intuition_electricity_today') | float %}
{% set solar = states('sensor.owl_intuition_solar_generated_today') | float %}
{% set last_grid_today = states('sensor.owl_grid_energy_today') | float %}
{% set last_grid_today = states('sensor.owl_grid_energy_today') | float(default=0) %}
{% if (float(elec) - float(solar)) >= 0 %}
{% if ((float(elec) - float(solar)) > float(last_grid_today)) or ((float(elec) - float(solar)) < 1 ) %}
{{ float(elec) - float(solar) }}
{% else %}
{{ float(last_grid_today) }}
{% endif %}
{% endif %}
{% endif %}
{% endif %}
122 changes: 58 additions & 64 deletions custom_components/owlintuition/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_BROADCAST_ADDRESS,
CONF_BROADCAST_PORT,
CONF_HOST,
CONF_MODE,
CONF_MONITORED_CONDITIONS,
CONF_NAME,
CONF_PORT,
ELECTRIC_POTENTIAL_VOLT,
ENERGY_KILO_WATT_HOUR,
PERCENTAGE,
POWER_WATT,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
TEMP_CELSIUS,
CONF_BROADCAST_ADDRESS,
CONF_BROADCAST_PORT,
UnitOfElectricPotential,
UnitOfEnergy,
UnitOfPower,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
Expand All @@ -45,7 +45,7 @@
CONF_COST_ICON = 'cost_icon'

# OWL-specific constants
VERSION = '1.6.0'
VERSION = '1.7.0'
DEFAULT_NAME = 'OWL Intuition'
MODE_MONO = 'monophase'
MODE_TRI = 'triphase'
Expand Down Expand Up @@ -98,29 +98,29 @@
OWLCLASS_RELAYS ]

SENSOR_TYPES = {
SENSOR_ELECTRICITY_BATTERY: ['Electricity Battery', None, 'mdi:battery', OWLCLASS_ELECTRICITY, SensorDeviceClass.ENUM, SensorStateClass.MEASUREMENT],
SENSOR_ELECTRICITY_BATTERY: ['Electricity Battery', None, 'mdi:battery', OWLCLASS_ELECTRICITY, SensorDeviceClass.ENUM, None],
SENSOR_ELECTRICITY_BATTERY_LVL: ['Electricity Battery Level', PERCENTAGE, 'mdi:battery', OWLCLASS_ELECTRICITY, SensorDeviceClass.BATTERY, SensorStateClass.MEASUREMENT],
SENSOR_ELECTRICITY_RADIO: ['Electricity Radio', SIGNAL_STRENGTH_DECIBELS_MILLIWATT, 'mdi:signal', OWLCLASS_ELECTRICITY, SensorDeviceClass.SIGNAL_STRENGTH, SensorStateClass.MEASUREMENT],
SENSOR_ELECTRICITY_POWER: ['Electricity Power', POWER_WATT, 'mdi:flash', OWLCLASS_ELECTRICITY, SensorDeviceClass.POWER, SensorStateClass.MEASUREMENT],
SENSOR_ELECTRICITY_ENERGY_TODAY: ['Electricity Today', ENERGY_KILO_WATT_HOUR, 'mdi:flash', OWLCLASS_ELECTRICITY, SensorDeviceClass.ENERGY, SensorStateClass.TOTAL_INCREASING],
SENSOR_ELECTRICITY_COST_TODAY: ['Cost Today', None, 'mdi:coin', OWLCLASS_ELECTRICITY, SensorDeviceClass.MONETARY, SensorStateClass.TOTAL_INCREASING],
SENSOR_SOLAR_GPOWER: ['Solar Generating', POWER_WATT, 'mdi:flash', OWLCLASS_SOLAR, SensorDeviceClass.POWER, SensorStateClass.MEASUREMENT],
SENSOR_SOLAR_GENERGY_TODAY: ['Solar Generated Today', ENERGY_KILO_WATT_HOUR, 'mdi:flash', OWLCLASS_SOLAR, SensorDeviceClass.ENERGY, SensorStateClass.TOTAL_INCREASING],
SENSOR_SOLAR_EPOWER: ['Solar Exporting', POWER_WATT, 'mdi:flash', OWLCLASS_SOLAR, SensorDeviceClass.POWER, SensorStateClass.MEASUREMENT],
SENSOR_SOLAR_EENERGY_TODAY: ['Solar Exported Today', ENERGY_KILO_WATT_HOUR, 'mdi:flash', OWLCLASS_SOLAR, SensorDeviceClass.ENERGY, SensorStateClass.TOTAL_INCREASING],
SENSOR_HOTWATER_BATTERY: ['Hotwater Battery', None, 'mdi:battery', OWLCLASS_HOTWATER, SensorDeviceClass.ENUM, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_BATTERY_LVL: ['Hotwater Battery Level', ELECTRIC_POTENTIAL_VOLT, 'mdi:battery', OWLCLASS_HOTWATER, SensorDeviceClass.VOLTAGE, SensorStateClass.MEASUREMENT],
SENSOR_ELECTRICITY_POWER: ['Electricity Power', UnitOfPower.WATT, 'mdi:flash', OWLCLASS_ELECTRICITY, SensorDeviceClass.POWER, SensorStateClass.MEASUREMENT],
SENSOR_ELECTRICITY_ENERGY_TODAY: ['Electricity Today', UnitOfEnergy.KILO_WATT_HOUR, 'mdi:flash', OWLCLASS_ELECTRICITY, SensorDeviceClass.ENERGY, SensorStateClass.TOTAL_INCREASING],
SENSOR_ELECTRICITY_COST_TODAY: ['Cost Today', None, 'mdi:coin', OWLCLASS_ELECTRICITY, SensorDeviceClass.MONETARY, None],
SENSOR_SOLAR_GPOWER: ['Solar Generating', UnitOfPower.WATT, 'mdi:flash', OWLCLASS_SOLAR, SensorDeviceClass.POWER, SensorStateClass.MEASUREMENT],
SENSOR_SOLAR_GENERGY_TODAY: ['Solar Generated Today', UnitOfEnergy.KILO_WATT_HOUR, 'mdi:flash', OWLCLASS_SOLAR, SensorDeviceClass.ENERGY, SensorStateClass.TOTAL_INCREASING],
SENSOR_SOLAR_EPOWER: ['Solar Exporting', UnitOfPower.WATT, 'mdi:flash', OWLCLASS_SOLAR, SensorDeviceClass.POWER, SensorStateClass.MEASUREMENT],
SENSOR_SOLAR_EENERGY_TODAY: ['Solar Exported Today', UnitOfEnergy.KILO_WATT_HOUR, 'mdi:flash', OWLCLASS_SOLAR, SensorDeviceClass.ENERGY, SensorStateClass.TOTAL_INCREASING],
SENSOR_HOTWATER_BATTERY: ['Hotwater Battery', None, 'mdi:battery', OWLCLASS_HOTWATER, SensorDeviceClass.ENUM, None],
SENSOR_HOTWATER_BATTERY_LVL: ['Hotwater Battery Level', UnitOfElectricPotential.VOLT, 'mdi:battery', OWLCLASS_HOTWATER, SensorDeviceClass.VOLTAGE, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_RADIO: ['Hotwater Radio', SIGNAL_STRENGTH_DECIBELS_MILLIWATT, 'mdi:signal', OWLCLASS_HOTWATER, SensorDeviceClass.SIGNAL_STRENGTH, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_CURRENT: ['Hotwater Temperature', TEMP_CELSIUS, 'mdi:thermometer', OWLCLASS_HOTWATER, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_REQUIRED: ['Hotwater Required', TEMP_CELSIUS, 'mdi:thermostat', OWLCLASS_HOTWATER, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_AMBIENT: ['Hotwater Ambient', TEMP_CELSIUS, 'mdi:thermometer', OWLCLASS_HOTWATER, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_STATE: ['Hotwater State', None, 'mdi:information-outline', OWLCLASS_HOTWATER, SensorDeviceClass.ENUM, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_BATTERY: ['Heating Battery', None, 'mdi:battery', OWLCLASS_HEATING, SensorDeviceClass.ENUM, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_BATTERY_LVL: ['Heating Battery Level', ELECTRIC_POTENTIAL_VOLT, 'mdi:battery', OWLCLASS_HEATING, SensorDeviceClass.VOLTAGE, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_CURRENT: ['Hotwater Temperature', UnitOfTemperature.CELSIUS, 'mdi:thermometer', OWLCLASS_HOTWATER, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_REQUIRED: ['Hotwater Required', UnitOfTemperature.CELSIUS, 'mdi:thermostat', OWLCLASS_HOTWATER, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_AMBIENT: ['Hotwater Ambient', UnitOfTemperature.CELSIUS, 'mdi:thermometer', OWLCLASS_HOTWATER, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HOTWATER_STATE: ['Hotwater State', None, 'mdi:information-outline', OWLCLASS_HOTWATER, SensorDeviceClass.ENUM, None],
SENSOR_HEATING_BATTERY: ['Heating Battery', None, 'mdi:battery', OWLCLASS_HEATING, SensorDeviceClass.ENUM, None],
SENSOR_HEATING_BATTERY_LVL: ['Heating Battery Level', UnitOfElectricPotential.VOLT, 'mdi:battery', OWLCLASS_HEATING, SensorDeviceClass.VOLTAGE, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_RADIO: ['Heating Radio', SIGNAL_STRENGTH_DECIBELS_MILLIWATT, 'mdi:signal', OWLCLASS_HEATING, SensorDeviceClass.SIGNAL_STRENGTH, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_CURRENT: ['Heating Temperature', TEMP_CELSIUS, 'mdi:thermometer', OWLCLASS_HEATING, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_REQUIRED: ['Heating Required', TEMP_CELSIUS, 'mdi:thermostat', OWLCLASS_HEATING, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_STATE: ['Heating State', None, 'mdi:information-outline', OWLCLASS_HEATING, SensorDeviceClass.ENUM, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_CURRENT: ['Heating Temperature', UnitOfTemperature.CELSIUS, 'mdi:thermometer', OWLCLASS_HEATING, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_REQUIRED: ['Heating Required', UnitOfTemperature.CELSIUS, 'mdi:thermostat', OWLCLASS_HEATING, SensorDeviceClass.TEMPERATURE, SensorStateClass.MEASUREMENT],
SENSOR_HEATING_STATE: ['Heating State', None, 'mdi:information-outline', OWLCLASS_HEATING, SensorDeviceClass.ENUM, None],
SENSOR_RELAYS_RADIO: ['Relays Radio', SIGNAL_STRENGTH_DECIBELS_MILLIWATT, 'mdi:signal', OWLCLASS_RELAYS, SensorDeviceClass.SIGNAL_STRENGTH, SensorStateClass.MEASUREMENT],
}

Expand Down Expand Up @@ -288,7 +288,6 @@ def __init__(self, owldata, sensor_name, sensor_type, phase=0, zone=1, zones_cou
self._name += f' P{phase}'
self._zone = zone
self._name_zone_updated = (zones_count == 1)
self._state = None
self._attr_attribution = POWERED_BY
self._attr_native_unit_of_measurement = SENSOR_TYPES[sensor_type][1]
self._owl_class = SENSOR_TYPES[sensor_type][3]
Expand All @@ -300,11 +299,6 @@ def _attr_name(self):
"""Return the current name for this sensor."""
return self._name

@property
def _attr_native_value(self):
"""Return the current value for this sensor."""
return self._state

@property
def icon(self):
"""Return the icon for this entity."""
Expand All @@ -331,105 +325,105 @@ def update(self):

glpatcern marked this conversation as resolved.
Show resolved Hide resolved
# Radio sensors
if self._attr_device_class == SensorDeviceClass.SIGNAL_STRENGTH:
self._state = int(xml.find('signal').attrib['rssi'])
self._attr_native_value = int(xml.find('signal').attrib['rssi'])
elif self._sensor_type == SENSOR_ELECTRICITY_BATTERY_LVL:
# Battery level in % for OWLCLASS_ELECTRICITY, mV for others
self._state = int(xml.find("battery").attrib['level'][:-1])
self._attr_native_value = int(xml.find("battery").attrib['level'][:-1])
elif self._sensor_type in [SENSOR_HOTWATER_BATTERY_LVL, SENSOR_HEATING_BATTERY_LVL]:
self._state = round(float(xml.find("battery").attrib['level'])/1000, 2)
self._attr_native_value = round(float(xml.find("battery").attrib['level'])/1000, 2)
elif self._sensor_type == SENSOR_ELECTRICITY_BATTERY:
batt_lvl = int(xml.find("battery").attrib['level'][:-1])
if batt_lvl > 90:
self._state = 'High'
self._attr_native_value = 'High'
elif batt_lvl > 30:
self._state = 'Medium'
self._attr_native_value = 'Medium'
elif batt_lvl > 10:
self._state = 'Low'
self._attr_native_value = 'Low'
else:
self._state = 'Very Low'
self._attr_native_value = 'Very Low'
elif self._sensor_type in [SENSOR_HOTWATER_BATTERY, SENSOR_HEATING_BATTERY]:
# 2670mV = 66%
# 2780mV = 76%
batt_lvl = int(xml.find("battery").attrib['level'])
if batt_lvl > 2900:
self._state = 'High'
self._attr_native_value = 'High'
elif batt_lvl > 2750:
self._state = 'Medium'
self._attr_native_value = 'Medium'
elif batt_lvl > 2600:
self._state = 'Low'
self._attr_native_value = 'Low'
else:
self._state = 'Very Low'
self._attr_native_value = 'Very Low'

# Electricity sensors
elif self._sensor_type == SENSOR_ELECTRICITY_POWER:
if self._phase == 0:
# xml_ver undefined for older version
if xml_ver is None:
self._state = int(float(xml.find('chan/curr').text))
self._attr_native_value = int(float(xml.find('chan/curr').text))
else:
self._state = int(float(xml.find('property/current/watts').text))
self._attr_native_value = int(float(xml.find('property/current/watts').text))
else:
if xml_ver is None:
self._state = int(float(xml.findall('chan')[self._phase-1].
self._attr_native_value = int(float(xml.findall('chan')[self._phase-1].
find('curr').text))
else:
self._state = int(float(xml.find('channels').
self._attr_native_value = int(float(xml.find('channels').
findall('chan')[self._phase-1].
find('curr').text))
elif self._sensor_type == SENSOR_ELECTRICITY_ENERGY_TODAY:
if self._phase == 0:
# xml_ver undefined for older version
if xml_ver is None:
self._state = round(float(xml.find('chan/day').text)/1000,2)
self._attr_native_value = round(float(xml.find('chan/day').text)/1000,2)
else:
self._state = round(float(xml.find('property/day/wh').text)/1000, 2)
self._attr_native_value = round(float(xml.find('property/day/wh').text)/1000, 2)
else:
if xml_ver is None:
self._state = round(float(xml.findall('chan')[self._phase-1].
self._attr_native_value = round(float(xml.findall('chan')[self._phase-1].
find('day').text)/1000, 2)
else:
self._state = round(float(xml.find('channels').
self._attr_native_value = round(float(xml.find('channels').
findall('chan')[self._phase-1].
find('day').text)/1000, 2)
elif self._sensor_type == SENSOR_ELECTRICITY_COST_TODAY:
# xml_ver undefined for older version
if xml_ver is None:
self._state = 0
self._attr_native_value = 0
else:
# the measure comes in cent. of the configured currency
self._state = round(float(xml.find('property/day/cost').text)/100, 3)
self._attr_native_value = round(float(xml.find('property/day/cost').text)/100, 3)

# Solar sensors
elif self._sensor_type == SENSOR_SOLAR_GPOWER:
self._state = int(float(xml.find('current/generating').text))
self._attr_native_value = int(float(xml.find('current/generating').text))
elif self._sensor_type == SENSOR_SOLAR_EPOWER:
self._state = int(float(xml.find('current/exporting').text))
self._attr_native_value = int(float(xml.find('current/exporting').text))
elif self._sensor_type == SENSOR_SOLAR_GENERGY_TODAY:
self._state = round(float(xml.find('day/generated').text)/1000, 2)
self._attr_native_value = round(float(xml.find('day/generated').text)/1000, 2)
elif self._sensor_type == SENSOR_SOLAR_EENERGY_TODAY:
self._state = round(float(xml.find('day/exported').text)/1000, 2)
self._attr_native_value = round(float(xml.find('day/exported').text)/1000, 2)

# Hot water sensors
if self._sensor_type == SENSOR_HOTWATER_CURRENT:
self._state = round(float(xml.find('temperature/current').text),1)
self._attr_native_value = round(float(xml.find('temperature/current').text),1)
elif self._sensor_type == SENSOR_HOTWATER_REQUIRED:
self._state = float(xml.find('temperature/required').text)
self._attr_native_value = float(xml.find('temperature/required').text)
elif self._sensor_type == SENSOR_HOTWATER_AMBIENT:
self._state = float(xml.find('temperature/ambient').text)
self._attr_native_value = float(xml.find('temperature/ambient').text)
elif self._sensor_type == SENSOR_HOTWATER_STATE:
# Heating state reported in version 2 and up
if xml_ver is not None:
self._state = HOTWATER_STATE[int(xml.find('temperature').attrib['state'])]
self._attr_native_value = HOTWATER_STATE[int(xml.find('temperature').attrib['state'])]

# Heating Sensors
elif self._sensor_type == SENSOR_HEATING_CURRENT:
self._state = round(float(xml.find('temperature/current').text),1)
self._attr_native_value = round(float(xml.find('temperature/current').text),1)
elif self._sensor_type == SENSOR_HEATING_REQUIRED:
self._state = float(xml.find('temperature/required').text)
self._attr_native_value = float(xml.find('temperature/required').text)
elif self._sensor_type == SENSOR_HEATING_STATE:
# Heating state reported in version 2 and up
if xml_ver is not None:
self._state = HEATING_STATE[int(xml.find('temperature').attrib['state'])]
self._attr_native_value = HEATING_STATE[int(xml.find('temperature').attrib['state'])]


class OwlStateUpdater(asyncio.DatagramProtocol):
Expand Down