HomeAssistant-Mods / home-assistant-miele

Miele integration for Home assistant
145 stars 30 forks source link

no remaining time entity #1

Closed Bascht74 closed 3 years ago

Bascht74 commented 3 years ago

Hi there,

I tried the new integration, but I don’t get entites for the remaining time. Is there something wront or am I looking at the wrong spot…?

here is the dishwasher:

{
  "000105104xxx": {
    "ident": {
      "type": {
        "key_localized": "Gerätetyp",
        "value_raw": 7,
        "value_localized": "Geschirrspüler"
      },
      "deviceName": "",
      "deviceIdentLabel": {
        "fabNumber": "000105104xxx",
        "fabIndex": "64",
        "techType": "G7565",
        "matNumber": "10992350",
        "swids": [
          "4921",
          "20492",
          "25166",
          "4465",
          "25318",
          "4928",
          "20475",
          "25266",
          "4875",
          "20366",
          "20462"
        ]
      },
      "xkmIdentLabel": {
        "techType": "EK037",
        "releaseVersion": "03.65"
      }
    },
    "state": {
      "ProgramID": {
        "value_raw": 1,
        "value_localized": "Intensiv",
        "key_localized": "Programmbezeichnung"
      },
      "status": {
        "value_raw": 5,
        "value_localized": "In Betrieb",
        "key_localized": "Status"
      },
      "programType": {
        "value_raw": 0,
        "value_localized": "Programm",
        "key_localized": "Programmart"
      },
      "programPhase": {
        "value_raw": 1795,
        "value_localized": "Reinigen",
        "key_localized": "Programmphase"
      },
      "remainingTime": [***
        2,
        24
      ],
      "startTime": [
        0,
        0
      ],
      "targetTemperature": [
        {
          "value_raw": -32768,
          "value_localized": null,
          "unit": "Celsius"
        },
        {
          "value_raw": -32768,
          "value_localized": null,
          "unit": "Celsius"
        },
        {
          "value_raw": -32768,
          "value_localized": null,
          "unit": "Celsius"
        }
      ],
      "temperature": [
        {
          "value_raw": -32768,
          "value_localized": null,
          "unit": "Celsius"
        },
        {
          "value_raw": -32768,
          "value_localized": null,
          "unit": "Celsius"
        },
        {
          "value_raw": -32768,
          "value_localized": null,
          "unit": "Celsius"
        }
      ],
      "signalInfo": false,
      "signalFailure": false,
      "signalDoor": false,
      "remoteEnable": {
        "fullRemoteControl": true,
        "smartGrid": false,
        "mobileStart": true
      },
      "light": 2,
      "elapsedTime": [
        0,
        0
      ],
      "spinningSpeed": {
        "unit": "U/min",
        "value_raw": null,
        "value_localized": null,
        "key_localized": "Schleuderdrehzahl"
      },
      "dryingStep": {
        "value_raw": null,
        "value_localized": "",
        "key_localized": "Trockenstufe"
      },
      "ventilationStep": {
        "value_raw": null,
        "value_localized": "",
        "key_localized": "Lüfterstufe"
      },
      "plateStep": [],
      "ecoFeedback": {
        "currentWaterConsumption": {
          "unit": "l",
          "value": 4
        },
        "currentEnergyConsumption": {
          "unit": "kWh",
          "value": 0.3
        },
        "waterForecast": 0.5,
        "energyForecast": 0.7
      },
      "batteryLevel": null
    }
  }
...

configuration.yaml: miele:

client_id: 7cc5649a-af44-4ee3-bf74-b0axxxxxxxxx
    client_secret: pZdgX2qXQo2MmXExL3aDSR7bTTxxxxxxxxx
    language: de

21bd3db25b748096472af28de99a22fa536455ef

Log:

2020-11-30 15:21:34 INFO (MainThread) [custom_components.miele.config_flow] Successfully authenticated
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.components.sensor] Setting up sensor.miele
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.components.binary_sensor] Setting up binary_sensor.miele
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX_program_id
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX_elapsed
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX_finish_time
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX_light
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX_program_phase
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX_program_type
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX_spinning_speed
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX_start
2020-11-30 15:21:34 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new sensor.miele entity: sensor.dishwasher_000105104XXX
2020-11-30 15:21:35 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new binary_sensor.miele entity: binary_sensor.dishwasher_000105104XXX_full_remote_control
2020-11-30 15:21:35 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new binary_sensor.miele entity: binary_sensor.dishwasher_000105104XXX_signal_door
2020-11-30 15:21:35 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new binary_sensor.miele entity: binary_sensor.dishwasher_000105104XXX_signal_failure
2020-11-30 15:21:35 INFO (MainThread) [homeassistant.helpers.entity_registry] Registered new binary_sensor.miele entity: binary_sensor.dishwasher_000105104XXX_signal_info

As you can see the remainint time is there in Miele API, but the integration doesn't create an entity for it. I have no clue... Maybe you could look into that problem. Thx

Sebastian

kloknibor commented 3 years ago

Hmm that is weird, on my install this works and it's also in the code the same way as elapsed and finish time:

        if 'remainingTime' in device_state:
            sensors.append(MieleTimeSensor(hass, device, 'remainingTime'))
        if 'startTime' in device_state:
            sensors.append(MieleTimeSensor(hass, device, 'startTime'))
        if 'elapsedTime' in device_state:
            sensors.append(MieleTimeSensor(hass, device, 'elapsedTime'))

What you can try is replacing sensor.py with the following code :

import logging

from datetime import timedelta, datetime

from homeassistant.helpers.entity import Entity

from custom_components.miele import DOMAIN as MIELE_DOMAIN, DATA_DEVICES

PLATFORMS = ['miele']

_LOGGER = logging.getLogger(__name__)

ALL_DEVICES = []

def _map_key(key):
    if key == 'status':
        return 'Status'
    elif key == 'programType':
        return 'Program Type'
    elif key == 'programPhase':
        return 'Program Phase'
    elif key == 'targetTemperature':
        return 'Target Temperature'
    elif key == 'temperature':
        return 'Temperature'
    elif key == 'remainingTime':
        return 'Remaining Time'
    elif key == 'elapsedTime':
        return 'Elapsed Time'
    elif key == 'startTime':
        return 'Start Time'

def _to_seconds(time_array):
    if len(time_array) == 3:
        return time_array[0] * 3600 + time_array[1] * 60 + time_array[2]
    elif len(time_array) == 2:
        return time_array[0] * 3600 + time_array[1] * 60
    else:
        return 0

# pylint: disable=W0612
def setup_platform(hass, config, add_devices, discovery_info=None):
    global ALL_DEVICES

    devices = hass.data[MIELE_DOMAIN][DATA_DEVICES]
    for k, device in devices.items():
        device_state = device['state']

        sensors = []
        if 'status' in device_state:
            sensors.append(MieleStatusSensor(hass, device, 'status'))

        if 'targetTemperature' in device_state:
            for i, val in enumerate(device_state['targetTemperature']):
                sensors.append(MieleTemperatureSensor(hass, device, 'targetTemperature', i))
        if 'temperature' in device_state:
            for i, val in enumerate(device_state['temperature']):
                sensors.append(MieleTemperatureSensor(hass, device, 'temperature', i))

        if 'remainingTime' in device_state:
            _LOGGER.debug(' trying to add remaining time, device_state is : {}'.format(device_state))
            sensors.append(MieleTimeSensor(hass, device, 'remainingTime'))
        if 'startTime' in device_state:
            sensors.append(MieleTimeSensor(hass, device, 'startTime'))
        if 'elapsedTime' in device_state:
            sensors.append(MieleTimeSensor(hass, device, 'elapsedTime'))

        add_devices(sensors)
        ALL_DEVICES = ALL_DEVICES + sensors

def update_device_state():
    for device in ALL_DEVICES:
        device.async_schedule_update_ha_state(True)

class MieleRawSensor(Entity):

    def __init__(self, hass, device, key):
        self._hass = hass
        self._device = device
        self._key = key

    @property
    def device_id(self):
        """Return the unique ID for this sensor."""
        return self._device['ident']['deviceIdentLabel']['fabNumber']

    @property
    def unique_id(self):
        """Return the unique ID for this sensor."""
        return self.device_id + '_' + self._key

    @property
    def name(self):
        """Return the name of the sensor."""
        ident = self._device['ident']

        result = ident['deviceName']
        if len(result) == 0:
            return ident['type']['value_localized'] + ' ' + _map_key(self._key)
        else:
            return result + ' ' + _map_key(self._key)

    @property
    def state(self):
        """Return the state of the sensor."""

        return self._device['state'][self._key]['value_raw']

    async def async_update(self): 
        if not self.device_id in self._hass.data[MIELE_DOMAIN][DATA_DEVICES]:
            _LOGGER.debug('Miele device disappeared: {}'.format(self.device_id))
        else:
            self._device = self._hass.data[MIELE_DOMAIN][DATA_DEVICES][self.device_id]

class MieleStatusSensor(MieleRawSensor):
    def __init(self, client, device, key):
        pass

    @property
    def state(self):
        """Return the state of the sensor."""
        result = self._device['state']['status']['value_localized']
        if result == None:
            result = self._device['state']['status']['value_raw']

        return result

    @property
    def device_state_attributes(self):
        """Attributes."""
        device_state = self._device['state']

        attributes = {}
        if 'programType' in device_state:
            attributes['programType'] = device_state['programType']['value_localized']
            attributes['rawProgramType'] = device_state['programType']['value_raw']

        if 'programPhase' in device_state:
            attributes['programPhase'] = device_state['programPhase']['value_localized']
            attributes['rawProgramPhase'] = device_state['programPhase']['value_raw']

        if 'dryingStep' in device_state:
            attributes['dryingStep'] = device_state['dryingStep']['value_localized']
            attributes['rawDryingStep'] = device_state['dryingStep']['value_raw']

        if 'spinningSpeed' in device_state:
            attributes['spinningSpeed'] = device_state['spinningSpeed']['value_localized']
            attributes['rawSpinningSpeed'] = device_state['spinningSpeed']['value_raw']

        if 'ventilationStep' in device_state:
            attributes['ventilationStep'] = device_state['ventilationStep']['value_localized']
            attributes['rawVentilationStep'] = device_state['ventilationStep']['value_raw']

        if 'plateStep' in device_state:
            plate_steps = 1
            for plateStep in device_state['plateStep']:
                attributes['plateStep'+str(plate_steps)] = plateStep['value_localized']
                attributes['rawPlateStep'+str(plate_steps)] = plateStep['value_raw']
                plate_steps += 1

        if 'ecoFeedback' in device_state and device_state['ecoFeedback'] is not None:
            if 'currentWaterConsumption' in device_state['ecoFeedback']:
                attributes['currentWaterConsumption'] = device_state['ecoFeedback']['currentWaterConsumption']['value']
                attributes['currentWaterConsumptionUnit'] = device_state['ecoFeedback']['currentWaterConsumption']['unit']
            if 'currentEnergyConsumption' in device_state['ecoFeedback']:
                attributes['currentEnergyConsumption'] = device_state['ecoFeedback']['currentEnergyConsumption']['value']
                attributes['currentEnergyConsumptionUnit'] = device_state['ecoFeedback']['currentEnergyConsumption']['unit']
            if 'waterForecast' in device_state['ecoFeedback']:
                attributes['waterForecast'] = device_state['ecoFeedback']['waterForecast']
            if 'energyForecast' in device_state['ecoFeedback']:
                attributes['energyForecast'] = device_state['ecoFeedback']['energyForecast']

        # Programs will only be running of both remainingTime and elapsedTime indicate 
        # a value > 0
        if 'remainingTime' in device_state and 'elapsedTime' in device_state:
            remainingTime = _to_seconds(device_state['remainingTime'])
            elapsedTime = _to_seconds(device_state['elapsedTime'])

            if 'startTime' in device_state:
                startTime = _to_seconds(device_state['startTime'])
            else:
                startTime = 0

            # Calculate progress
            if (elapsedTime + remainingTime) == 0:
                attributes['progress'] = None
            else:
                attributes['progress'] = round(elapsedTime / (elapsedTime + remainingTime) * 100, 1)

            # Calculate end time
            if remainingTime == 0:
                attributes['finishTime'] = None
            else:
                now = datetime.now()
                attributes['finishTime'] = (now + timedelta(seconds=startTime) + timedelta(seconds=remainingTime)).strftime('%H:%M')

          # Calculate start time
            if startTime == 0:
                now = datetime.now()
                attributes['kickoffTime'] = (now - timedelta(seconds=elapsedTime)).strftime('%H:%M')
            else:
                now = datetime.now()
                attributes['kickoffTime'] = (now + timedelta(seconds=startTime)).strftime('%H:%M')

        return attributes

class MieleTimeSensor(MieleRawSensor):
    def __init(self, hass, device, key):
        pass

    @property
    def state(self):
        """Return the state of the sensor."""
        state_value = self._device['state'][self._key]
        if len(state_value) != 2:
            return None
        else:
            return '{:02d}:{:02d}'.format(state_value[0], state_value[1])

class MieleTemperatureSensor(Entity):

    def __init__(self, hass, device, key, index):
        self._hass = hass
        self._device = device
        self._key = key
        self._index = index

        """Fix for assertion error by removing None from device"""
        value_raw = self._device['state'][self._key][self._index]['value_raw']
        value_localized = self._device['state'][self._key][self._index]['value_localized']
        if value_raw == "-32768":
            self._device['state'][self._key][self._index]['value_raw'] = 0
        if value_localized == "null":
            self._device['state'][self._key][self._index]['value_localized'] = 0

    @property
    def device_id(self):
        """Return the unique ID for this sensor."""
        return self._device['ident']['deviceIdentLabel']['fabNumber']

    @property
    def unique_id(self):
        """Return the unique ID for this sensor."""
        return self.device_id + '_' + self._key + '_{}'.format(self._index)

    @property
    def name(self):
        """Return the name of the sensor."""
        ident = self._device['ident']

        result = ident['deviceName']
        if len(result) == 0:
            return '{} {} {}'.format(ident['type']['value_localized'], _map_key(self._key), self._index)
        else:
            return '{} {} {}'.format(result, _map_key(self._key), self._index)

    @property
    def state(self):
        """Return the state of the sensor."""
        state_value = self._device['state'][self._key][self._index]['value_raw']
        if state_value == -32768:
            return None
        else:
            return state_value / 100

    @property
    def unit_of_measurement(self):
        """Return the unit of measurement of this entity, if any."""
        if self._device['state'][self._key][self._index]['unit'] == 'Celsius':
            return "°C"
        elif self._device['state'][self._key][self._index]['unit'] == 'Fahrenheit':
            return "°F"

    @property
    def device_class(self):
        return "temperature"

    async def async_update(self): 
        if not self.device_id in self._hass.data[MIELE_DOMAIN][DATA_DEVICES]:
            _LOGGER.debug(' Miele device disappeared: {}'.format(self.device_id))
        else:
            self._device = self._hass.data[MIELE_DOMAIN][DATA_DEVICES][self.device_id]

this should put a logging output of : trying to add remaining time, device_state is if this line is present it does try to add it.

kloknibor commented 3 years ago

You can also try to get additional log output by adding this to configuration.yaml :

logger:
  default: critical
  logs:
    custom_components.miele: debug

also i wouldn't share your client_id and client_secret add github ;-)!

Bascht74 commented 3 years ago

also i wouldn't share your client_id and client_secret add github ;-)!

I didn't do that completly (lot of xxxxxxxxx). Or should I delete everything?

client_id: 7cc5649a-af44-4ee3-bf74-b0axxxxxxxxx
    client_secret: pZdgX2qXQo2MmXExL3aDSR7bTTxxxxxxxxx
    language: de

Trying right now. Thanks for your help.

Bascht74 commented 3 years ago

I replaced the sensor.py code in /config/custom_components/miele/ with your new code.

Result so far: Unbenannt

But i see nothing in the log about it.

logger:
  default: critical
  logs:
    custom_components.miele: debug

Unbenannt2

kloknibor commented 3 years ago

How did you install this software? Could you please try to install this by HACS (manually add this repo for now, I'm currently waiting to get included in the dafault) and try again? Because what you showed me happens I cannot explain this with the current code...

You have devices like _start and _elapsed but these never get made by the software, however _start_time does get made. But that's not what I see in your screenshots. If you want you could also send me your miele token privately and I can check what happens if I run your devices on my test environment.

Bascht74 commented 3 years ago

How did you install this software? Could you please try to install this by HACS (manually add this repo for now, I'm currently waiting to get included in the dafault) and try again?

I did via HACS (manual added repro, as I remember). I will delete it via HACS, clean up the files and re-install again...

Bascht74 commented 3 years ago
  1. Installed it via HACS.
  2. Restarted.
  3. Added the lines to configuration.yaml:
miele:
  client_id: 7cc5649a-af44-4ee3-bf74-....
  client_secret: pZdgX2qXQo2MmXE....
  language: de
  1. Checked the configuration an getting:
    
    Configuration invalid

Invalid config for [miele]: [language] is an invalid option for [miele]. Check: miele->miele->language. (See /config/configuration.yaml, line 21).


5. Leave out the language setting right now.

miele: client_id: 7cc5649a-af44-4ee3-bf74-.... client_secret: pZdgX2qXQo2MmXE....


6. Checked configuration again. Now: valid
7. Restarted
8. Opened the configurator, logged in at Miele. 
9. Entities are there, so is the remaining time.

I don't know how, but doing everything from scratsch it is there:
Washing Machine Remaining Time sensor.washing_machine_remaining_time Miele@home

So it works now! Thanks for your help.

Two Questions:
1. How do I set the language right?
2. Are you planing to list the devices at devices as well? Right now I can only see entities.

Sebastian
Bascht74 commented 3 years ago

First question: nevermind. RTFM ... lang insteat of language...

kloknibor commented 3 years ago

Hi Sebastian, possibly in the future. My first priority is fixing a bug where some devices will not show up if they did not have a valid value when home assistant started. At the same time, I'm also working on making the installation simpler for everyone! Currently trying to get added to the HACS default repo. If you have it working now and want to help you can go over: https://github.com/hacs/default/pull/718 to review and then approve this integration to be added to the HACS default repo ;-)!