Skip to content

Commit

Permalink
Merge pull request #37 from shortbloke/FixIssue#36
Browse files Browse the repository at this point in the history
Fix issues with sensors not updating after HA 2024.1 upgrade
  • Loading branch information
glpatcern authored Jan 16, 2024
2 parents c7c2082 + 994b0fa commit b479dc3
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 79 deletions.
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
16 changes: 13 additions & 3 deletions custom_components/owlintuition/owl_intuition.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
####################################################
# 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
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 %}
138 changes: 62 additions & 76 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 @@ -283,33 +283,18 @@ def __init__(self, owldata, sensor_name, sensor_type, phase=0, zone=1, zones_cou
self._owldata = owldata
self._sensor_type = sensor_type
self._phase = phase
self._name = f'{sensor_name} {SENSOR_TYPES[sensor_type][0]}'
self._attr_name = f'{sensor_name} {SENSOR_TYPES[sensor_type][0]}'
if phase > 0:
self._name += f' P{phase}'
self._attr_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._attr_icon = SENSOR_TYPES[sensor_type][2]
self._owl_class = SENSOR_TYPES[sensor_type][3]
self._attr_device_class = SENSOR_TYPES[sensor_type][4]
self._attr_state_class = SENSOR_TYPES[sensor_type][5]

@property
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."""
return SENSOR_TYPES[self._sensor_type][2]

def update(self):
"""Retrieve the latest value for this sensor."""
self._owldata.update()
Expand All @@ -329,107 +314,108 @@ def update(self):
# fallback to the first one
xml = xml.find('zones/zone')

# Update the state of the current sensor: we use _attr_native_value and not _state because of #36
# 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

0 comments on commit b479dc3

Please sign in to comment.