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

SmartThings Air Conditioner - No humidity value #57509

Closed
dimmuboy opened this issue Oct 11, 2021 · 24 comments
Closed

SmartThings Air Conditioner - No humidity value #57509

dimmuboy opened this issue Oct 11, 2021 · 24 comments

Comments

@dimmuboy
Copy link

The problem

My AC in does not show any humidity value in Home Assistant but when I can get it via pysmartthings manually

What is version of Home Assistant Core has the issue?

2021.10.2

What was the last working version of Home Assistant Core?

No response

What type of installation are you running?

Home Assistant OS

Integration causing the issue

SmartThings

Link to integration documentation on our website

https://www.home-assistant.io/integrations/smartthings/

Example YAML snippet

No response

Anything in the logs that might be useful for us?

climate.bedroom_a_c
Bedroom A/C

hvac_modes: cool, dry, off, heat_cool, heat
min_temp: 7
max_temp: 35
fan_modes: auto, high, low, medium, turbo
current_temperature: 24
temperature: 22
fan_mode: auto
drlc_status_duration: 0
drlc_status_override: false
friendly_name: Bedroom A/C
supported_features: 9

Additional information

I use this code to get data from my AC via pysmartthings directly

#!/usr/bin/env python3
import aiohttp
import asyncio
import pysmartthings

token = '****'

async def print_devices():
    async with aiohttp.ClientSession() as session:
        api = pysmartthings.SmartThings(session, token)
        devices = await api.devices()
        for device in devices:
            print("{}: {}".format(device.device_id, device.label))
            await device.status.refresh()
            print("temperature: ",end = '')
            print(device.status.temperature)
            print("humidity: ",end = '')
            print(device.status.humidity)
            # print(device.status.switch)
            # print(device.status.level)


def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(print_devices())
    # loop.close()

if __name__ == '__main__':
    main()

and I got this without any problems

cff54***b14d: Bedroom A/C
temperature: 24.0
humidity: 28
0f19***8922: Living Room A/C
temperature: 24.0
humidity: 36
@probot-home-assistant
Copy link

smartthings documentation
smartthings source
(message by IssueLinks)

@probot-home-assistant
Copy link

Hey there @andrewsayre, mind taking a look at this issue as it has been labeled with an integration (smartthings) you are listed as a code owner for? Thanks!
(message by CodeOwnersMention)

@veista
Copy link

veista commented Nov 1, 2021

Hi, I ran in the same issue. There are two issues in the smartthings Integration. One is that in the climate.py the humidity attribute is not set. The other one is that the humidity and power meter capabilities are assigned to the climate instance, so they do not list as sensors, which in my opinion they should. Easy fix: Comment out the Capability.power_consumption_report, and Capability.relative_humidity_measurement, from climate.py

def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
    """Return all capabilities supported if minimum required are present."""
    supported = [
        Capability.air_conditioner_mode,
        Capability.demand_response_load_control,
        Capability.air_conditioner_fan_mode,
        # Capability.power_consumption_report,
        # Capability.relative_humidity_measurement,
        Capability.switch,
        Capability.temperature_measurement,
        Capability.thermostat,
        Capability.thermostat_cooling_setpoint,
        Capability.thermostat_fan_mode,
        Capability.thermostat_heating_setpoint,
        Capability.thermostat_mode,
        Capability.thermostat_operating_state,
    ]

and/or add:

@property
def current_humidity(self):
        """Return the current humidity."""
        return self._device.status.humidity

as the last lines in climate.py

you can do this by copying the repo, editing it and adding it as a custom component to Home Assistant

I will make a pull request soon, if this does not get fixed. There are some other minor issues that I'm working to fix too.

@veista
Copy link

veista commented Nov 1, 2021

@andrewsayre BTW is help needed adding custom.capabilities to pysmartthings? I am working on this for myself. There are so much capabilities missing from samsung air conditioners.

@dimmuboy
Copy link
Author

dimmuboy commented Nov 7, 2021

Thank you so much @veista I have it done via rest command for now because I don't touch source code of HA libs but it would be great to apply your changes in official code.

@metropt
Copy link

metropt commented Nov 9, 2021

Thank you @veista
It looks like pysmartthings developer it not active anymore. How do you will add the missing capabilities? I also miss the option to turn on or off the beep and light.

@veista
Copy link

veista commented Nov 10, 2021

You are welcome @metropt
It is weird that the volume is added only as sensor, but you can add all of those fixes without touching the core files. The light switch cannot be added to smartthings integration as is, because for some reason I do not understand, you do not get push updates from Execute, Data. This is maybe on purpose, but I can't find any omission regarding this. Instead you have to do it with a python script.

simplest form:

import requests

url = "https://api.smartthings.com/v1/devices/{YOUR DEVICE ID}/commands"

payload = "{  \"commands\": [{  \"component\": \"main\",  \"capability\": \"execute\",  \"command\": \"execute\",  \"arguments\": [\"mode/vs/0\",{\"x.com.samsung.da.options\": [\"Light_On\"]}]}  ]}"
headers = {
  'Authorization': 'Bearer {YOUR ACCESS TOKEN}',
  'Content-Type': 'text/plain'
}

Light_On == Light turns off
Light_Off == Light turns on

If you want to go fancy you can poll the state by sending only:

payload = "{ \"commands\": [{ \"component\": \"main\", \"capability\": \"execute\", \"command\": \"execute\", \"arguments\": [\"mode/vs/0\"]} ]}

Then polling the Execute Data and parsing the Light_On or Light_Off from it, then return the result.

Easiest way to add the volume is adding it as a switch, copy the core repo from github, extract the smartthings folder.

Then edit the files and insert them into a folder named custom_components. Replace this in the switch.py

"""Support for switches through the SmartThings cloud API."""
from __future__ import annotations

from collections import namedtuple
from collections.abc import Sequence

from pysmartthings import Capability, Attribute
from pysmartthings.device import DeviceEntity

from homeassistant.components.switch import SwitchEntity

from . import SmartThingsEntity
from .const import DATA_BROKERS, DOMAIN

Map = namedtuple(
    "map",
    "attribute on_command off_command on_value off_value name extra_state_attributes",
)

CAPABILITY_TO_SWITCH = {
    Capability.switch: [
        Map(
            Attribute.switch,
            "switch_on",
            "switch_off",
            "on",
            "off",
            "Switch",
            None,
        )
    ],
    Capability.audio_volume: [
        Map(
            Attribute.volume,
            "set_volume",
            "set_volume",
            100,
            0,
            "Audio",
            None,
        )
    ],
}


async def async_setup_entry(hass, config_entry, async_add_entities):
    """Add switches for a config entry."""
    broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
    switches = []
    for device in broker.devices.values():
        for capability in broker.get_assigned(device.device_id, "switch"):
            maps = CAPABILITY_TO_SWITCH[capability]
            switches.extend(
                [
                    SmartThingsSwitch(
                        device,
                        m.attribute,
                        m.on_command,
                        m.off_command,
                        m.on_value,
                        m.off_value,
                        m.name,
                        m.extra_state_attributes,
                    )
                    for m in maps
                ]
            )

    async_add_entities(switches)


def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
    """Return all capabilities supported if minimum required are present."""
    # Must be able to be turned on/off.
    return [
        capability for capability in CAPABILITY_TO_SWITCH if capability in capabilities
    ]


class SmartThingsSwitch(SmartThingsEntity, SwitchEntity):
    """Define a SmartThings switch."""

    def __init__(
        self,
        device: DeviceEntity,
        attribute: str | None,
        on_command: str | None,
        off_command: str | None,
        on_value: str | int | None,
        off_value: str | int | None,
        name: str,
        extra_state_attributes: str | None,
    ) -> None:
        """Init the class."""
        super().__init__(device)
        self._attribute = attribute
        self._on_command = on_command
        self._off_command = off_command
        self._on_value = on_value
        self._off_value = off_value
        self._name = name
        self._extra_state_attributes = extra_state_attributes

    async def async_turn_off(self, **kwargs) -> None:
        """Turn the switch off."""
        if self._off_command is not None:
            if self._on_command == self._off_command:
                await getattr(self._device, self._off_command)(
                    self._off_value, set_status=True
                )
            else:
                await getattr(self._device, self._off_command)(set_status=True)

        # State is set optimistically in the command above, therefore update
        # the entity state ahead of receiving the confirming push updates
        self.async_write_ha_state()

    async def async_turn_on(self, **kwargs) -> None:
        """Turn the switch on."""
        if self._on_command is not None:
            if self._on_command == self._off_command:
                await getattr(self._device, self._on_command)(
                    self._on_value, set_status=True
                )
            else:
                await getattr(self._device, self._on_command)(set_status=True)
        # State is set optimistically in the command above, therefore update
        # the entity state ahead of receiving the confirming push updates
        self.async_write_ha_state()

    @property
    def name(self) -> str:
        """Return the name of the binary sensor."""
        return f"{self._device.label} {self._name}"

    @property
    def unique_id(self) -> str:
        """Return a unique ID."""
        if self._attribute is not None:
            return f"{self._device.device_id}.{self._attribute}"
        return f"{self._device.device_id}.{self._name}"

    @property
    def is_on(self) -> bool:
        """Return true if switch is on."""
        if self._attribute is not None:
            if self._device.status.attributes[self._attribute].value == self._on_value:
                return True
        return False

    @property
    def extra_state_attributes(self):
        """Return device specific state attributes."""
        state_attributes = {}
        if self._extra_state_attributes is not None:
            attributes = self._extra_state_attributes
            for attribute in attributes:
                value = getattr(self._device.status, attribute)
                if value is not None:
                    state_attributes[attribute] = value
        return state_attributes

I get the feeling that @andrewsayre will not accept the custom. capabilities into pysmartthings. If he doesn't then you have to edit the core files (device.py and capability.py) and you have to do this every time the core updates.

I'm thinking of making my own integration, but it would take a lot of time.

@veista
Copy link

veista commented Nov 10, 2021

@metropt though be warned, this is a bad example for the switch. If you have another device with audio volume capability it will also turn out as a switch. I have made better code with number entity for audio volume, but this is how I do it myself, since the audio volume is not used anywhere else. Also the AC only does something when at 0 or 100.

@veista
Copy link

veista commented Nov 11, 2021

@metropt If you want to do it the proper way:

Create a number.py file in the smartthings folder and put this in it:

"""Support for numbers through the SmartThings cloud API."""
from __future__ import annotations

from collections import namedtuple
from collections.abc import Sequence

from typing import Literal

from pysmartthings import Attribute, Capability
from pysmartthings.device import DeviceEntity

from homeassistant.components.number import NumberEntity, MODE_AUTO

from . import SmartThingsEntity
from .const import DATA_BROKERS, DOMAIN

from homeassistant.const import PERCENTAGE

Map = namedtuple(
    "map",
    "attribute command name unit_of_measurement min_value max_value step mode",
)

UNITS = {"%": PERCENTAGE}

CAPABILITY_TO_NUMBER = {
    Capability.audio_volume: [
        Map(
            Attribute.volume,
            "set_volume",
            "Audio Volume",
            PERCENTAGE,
            0,
            100,
            1,
            MODE_AUTO,
        )
    ],
}


async def async_setup_entry(hass, config_entry, async_add_entities):
    """Add numbers for a config entries."""
    broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
    numbers = []
    for device in broker.devices.values():
        for capability in broker.get_assigned(device.device_id, "number"):
            maps = CAPABILITY_TO_NUMBER[capability]
            numbers.extend(
                [
                    SmartThingsNumber(
                        device,
                        m.attribute,
                        m.command,
                        m.name,
                        m.unit_of_measurement,
                        m.min_value,
                        m.max_value,
                        m.step,
                        m.mode,
                    )
                    for m in maps
                ]
            )
    async_add_entities(numbers)


def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
    """Return all capabilities supported if minimum required are present."""
    # Must have a numeric value that is selectable.
    return [
        capability for capability in CAPABILITY_TO_NUMBER if capability in capabilities
    ]


class SmartThingsNumber(SmartThingsEntity, NumberEntity):
    """Define a SmartThings Number."""

    def __init__(
        self,
        device: DeviceEntity,
        attribute: str,
        command: str,
        name: str,
        unit_of_measurement: str | None,
        min_value: str | None,
        max_value: str | None,
        step: str | None,
        mode: str | None,
    ) -> None:
        """Init the class."""
        super().__init__(device)
        self._attribute = attribute
        self._command = command
        self._name = name
        self._attr_unit_of_measurement = unit_of_measurement
        self._attr_min_value = min_value
        self._attr_max_value = max_value
        self._attr_step = step
        self._attr_mode = mode

    async def async_set_value(self, value: float) -> None:
        """Turn the switch off."""
        await getattr(self._device, self._command)(int(value), set_status=True)
       
    @property
    def name(self) -> str:
        """Return the name of the binary sensor."""
        return f"{self._device.label} {self._name}"

    @property
    def unique_id(self) -> str:
        """Return a unique ID."""
        return f"{self._device.device_id}.{self._attribute}"

    @property
    def value(self) -> float:
        """Return  Value."""
        return self._device.status.attributes[self._attribute].value

    @property
    def min_value(self) -> float:
        """Define mimimum level."""
        return self._attr_min_value

    @property
    def max_value(self) -> float:
        """Define maximum level."""
        return self._attr_max_value

    @property
    def step(self) -> float:
        """Define stepping size"""
        return self._attr_step

    @property
    def unit_of_measurement(self) -> str | None:
        """Return unit of measurement"""
        unit = self._device.status.attributes[self._attribute].unit
        return UNITS.get(unit, unit) if unit else self._attr_unit_of_measurement

    @property
    def mode(self) -> Literal["auto", "slider", "box"]:
        """Return representation mode"""
        return self._attr_mode

then edit the const.py file and add the number device:

PLATFORMS = [
    "climate",
    "fan",
    "light",
    "lock",
    "cover",
    "number",
    "switch",
    "binary_sensor",
    "sensor",
    "scene",
] 

@veista
Copy link

veista commented Nov 11, 2021

@metropt actually I figured out why I wasn't getting the push updates from the Execute Data. But still it is not wise to add it as a switch if you have any other smartthings devices.

@veista
Copy link

veista commented Nov 11, 2021

But now I figured out how to add the all of the capabilities without modifying the pysmartthigs library. Will be back with a fullish support of the samsung AC:s. Still a lot of work but it is doable.

@veista
Copy link

veista commented Nov 13, 2021

@metropt , @dimmuboy Here is a custom library for you. https://github.com/veista/smartthings

@dimmuboy
Copy link
Author

@metropt , @dimmuboy Here is a custom library for you. https://github.com/veista/smartthings

Unbelievable! Great work @veista it looks very well 👍

@metropt
Copy link

metropt commented Nov 15, 2021

@veista thank you :) Is it possible to install it without HACS?

@veista
Copy link

veista commented Nov 15, 2021

Yes. Just copy the files to custom_components/smartthings and use normally from integrations

@dimmuboy
Copy link
Author

@veista you're crazy man! that works perfectly :)

@metropt
Copy link

metropt commented Nov 16, 2021

@veista after add the custom component do I need to add the devices again?
image

@veista
Copy link

veista commented Nov 16, 2021

Please raise any issues to https://github.com/veista/smartthings/issues. It is recommended to add the devices again. This removes any ghost entities automatically.

@metropt
Copy link

metropt commented Nov 16, 2021

Everything seems to be working and I didn't need to add the devices again :)

@metropt
Copy link

metropt commented Nov 16, 2021

I'm trying to understand the changes you made @veista. I can't understand why Capability.temperature_measurement should be a capability but humidity shouldn't.

The other one is that the humidity and power meter capabilities are assigned to the climate instance, so they do not list as sensors, which in my opinion they should.

@veista
Copy link

veista commented Nov 17, 2021

Ok. By default (in the default climate card) HA doesn't show these attributes (power and humidity) and that is my reasoning. Current temperature is shown so it would be redundant. You can add it yoursef by extracting the attribute in config yaml, or by editing the custom component. Also I think the default use case of power would be to add it to energy tab, this just makes things more convenient.

@gwheels
Copy link

gwheels commented Jan 13, 2022

At risk of thread drift, do you think this solution could address the no temperature or setpoint values on my Samsung refrigerator:
https://github.com/home-assistant/core/issues/61075?

I do have the smartthings integration, but don't have the smartthings folder in my custom_components folder, so none of the files mentioned above. Any insights?

@veista
Copy link

veista commented Jan 14, 2022

At risk of thread drift, do you think this solution could address the no temperature or setpoint values on my Samsung refrigerator: https://github.com/home-assistant/core/issues/61075?

I do have the smartthings integration, but don't have the smartthings folder in my custom_components folder, so none of the files mentioned above. Any insights?

You can create an issue here: https://github.com/veista/smartthings

@github-actions
Copy link

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.
Please make sure to update to the latest Home Assistant version and check if that solves the issue. Let us know if that works for you by adding a comment 👍
This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.

@github-actions github-actions bot added the stale label Apr 14, 2022
@github-actions github-actions bot locked and limited conversation to collaborators May 25, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants