appdaemon icon indicating copy to clipboard operation
appdaemon copied to clipboard

Got a ClientResponse error after updating from AppDaemon 0,17.10 to 0.17.12

Open wdriessen opened this issue 3 months ago • 2 comments

What happened?

I have some code that creates some energy sensors. This code worked fine for several years now, but after updating to AppDeamon version 0.17.12 I get an error when calling self.set_state()

The complete error logged after starting the code class below is as follows:

025-11-10 16:20:17.790386 INFO EnergyDailyUsage: Unexpected error setting sensor.delivered_low_day: argument of type 'ClientResponseError' is not iterable

My class is as follows:

import appdaemon.plugins.hass.hassapi as hass
import datetime
from pytz import timezone
from aiohttp import ClientResponseError

class EnergyDailyUsage(hass.Hass):

    def initialize(self):
        self.trace = False
        self._trace("initialize", "started")

        self.id_ch = self.args["consume_high"]
        self.id_cl = self.args["consume_low"]
        self.id_dh = self.args["deliver_high"]
        self.id_dl = self.args["deliver_low"]
        self.id_gas = self.args["gas"]

        self.startDay = {}
        self.startMonth = {}
        self.startYear = {}

        self.energyfilestore = self.get_app('EnergyFileStore')

        self.run_daily(self.callback, datetime.time(0, 0, 0))
        self.run_at(self.callback, datetime.datetime.now() + datetime.timedelta(seconds=2))
        self.run_in(self._timertick, 60)

        self.listen_state(self._tracemode, "input_boolean.trace_mode")
        self.turn_off("input_boolean.trace_mode")

    def _safe_float(self, value):
        try:
            return float(value)
        except (TypeError, ValueError):
            return 0.0

    def _tracemode(self, entity, attribute, old, new, kwargs):
        self.trace = new == 'on'
        self._trace("_tracemode()", f"Trace mode {'on' if self.trace else 'off'}")

    def callback(self, kwargs):
        self._trace("callback", "started")
        self._check_scope('day')
        self._check_scope('month')
        self._check_scope('year')

    def _check_scope(self, scope):
        data = self.energyfilestore.GetEnergyStart(scope == 'day', scope == 'month', scope == 'year')
        now = datetime.datetime.now(timezone('Europe/Amsterdam'))

        if "dt" in data:
            if (
                (scope == 'day' and data['dt'].date() != now.date()) or
                (scope == 'month' and data['dt'].month != now.month) or
                (scope == 'year' and data['dt'].year != now.year)
            ):
                self._trace(f"_check_{scope}", f"Different {scope.capitalize()}")
                self._store_startvalues(scope)
            else:
                self._trace(f"_check_{scope}", f"Same {scope.capitalize()}")
        else:
            self._store_startvalues(scope)

        setattr(self, f"start{scope.capitalize()}", data)

    def _store_startvalues(self, scope):
        tz = timezone('Europe/Amsterdam')
        now = datetime.datetime.now(tz=tz)
        record = {
            'dt': now,
            'gas': self.get_state(self.id_gas),
            'ch': self.get_state(self.id_ch),
            'cl': self.get_state(self.id_cl),
            'dh': self.get_state(self.id_dh),
            'dl': self.get_state(self.id_dl),
        }

        self.energyfilestore.SetEnergyStart(
            record,
            scope == 'day',
            scope == 'month',
            scope == 'year'
        )
        setattr(self, f"start{scope.capitalize()}",
                self.energyfilestore.GetEnergyStart(
                    scope == 'day',
                    scope == 'month',
                    scope == 'year'
                ))

    def _timertick(self, args):
        if self._check_if_values_ready():
            self._create_and_save('day', self.startDay)
            self._create_and_save('month', self.startMonth)
            self._create_and_save('year', self.startYear)
        self.run_in(self._timertick, 60)

    def _check_if_values_ready(self):
        for sensor in [self.id_ch, self.id_cl, self.id_dh, self.id_dl, self.id_gas]:
            if self.get_state(sensor) == 'unknown':
                return False
        return True

    def _create_and_save(self, scope, start_data):
        suffix = f"_{scope}"
        now_vals = {
            'ch': self._safe_float(self.get_state(self.id_ch)),
            'cl': self._safe_float(self.get_state(self.id_cl)),
            'dh': self._safe_float(self.get_state(self.id_dh)),
            'dl': self._safe_float(self.get_state(self.id_dl)),
            'gas': self._safe_float(self.get_state(self.id_gas)),
        }

        deltas = {k: now_vals[k] - self._safe_float(start_data.get(k)) for k in now_vals}

        if 'ch' in start_data:
            self._set_energy_sensor(f"sensor.consumed_high{suffix}", deltas['ch'], f"{scope.capitalize()}s Consumed Electricity High", "kWh")
        if 'cl' in start_data:
            self._set_energy_sensor(f"sensor.consumed_low{suffix}", deltas['cl'], f"{scope.capitalize()}s Consumed Electricity Low", "kWh")
        if 'dh' in start_data:
            self._set_energy_sensor(f"sensor.delivered_high{suffix}", deltas['dh'], f"{scope.capitalize()}s Delivered Electricity High", "kWh")
        if 'dl' in start_data:
            self._set_energy_sensor(f"sensor.delivered_low{suffix}", deltas['dl'], f"{scope.capitalize()}s Delivered Electricity Low", "kWh")
        if 'gas' in start_data:
            self._set_energy_sensor(f"sensor.consumed_gas{suffix}", deltas['gas'], f"{scope.capitalize()}s Consumed Gas", "m3")

        if 'ch' in start_data and 'cl' in start_data:
            consumed = deltas['ch'] + deltas['cl']
            self._set_energy_sensor(f"sensor.consumed{suffix}", consumed, f"{scope.capitalize()}s Consumed Electricity", "kWh")
        if 'dh' in start_data and 'dl' in start_data:
            delivered = deltas['dh'] + deltas['dl']
            self._set_energy_sensor(f"sensor.delivered{suffix}", delivered, f"{scope.capitalize()}s Delivered Electricity", "kWh")
        if all(k in start_data for k in ('ch', 'cl', 'dh', 'dl')):
            netto = (deltas['ch'] + deltas['cl']) - (deltas['dh'] + deltas['dl'])
            self._set_energy_sensor(f"sensor.netto{suffix}", netto, f"{scope.capitalize()}s Netto Electricity", "kWh")

    def _set_energy_sensor(self, name, value, friendly_name, unit):
        self._trace("_set_energy_sensor", f"Name = {name}")
        try:
            self.set_state(
                entity_id=name,
                state=round(value, 3),
                attributes={
                    "friendly_name": friendly_name,
                    "unit_of_measurement": unit
                }
            )
        except ClientResponseError as e:
            self.error(f"Failed to set state for {name}: {e.status} {e.message}")
        except Exception as e:
            self.error(f"Unexpected error setting {name}: {e}")

    def _trace(self, function, message):
        if self.trace:
            self.log(f"{function} -> {message}")

Gr, Will

Version

4.5.12

Installation type

Home Assistant add-on

Relevant log output


Relevant code in the app or config file that caused the issue


Anything else?

No response

wdriessen avatar Nov 17 '25 15:11 wdriessen

This should be fixed in dev, but will at least have a better error message if it's not.

jsl12 avatar Dec 02 '25 00:12 jsl12

If you need to fix it quickly you can ensure the value set to the entity is a string. Through str() for example.

self.set_state(
                entity_id=name,
                state=str(round(value, 3)),
                attributes={
                    "friendly_name": friendly_name,
                    "unit_of_measurement": unit
                }
            )

palinf avatar Dec 12 '25 12:12 palinf