hello do you put the code in esp?
I have also managed to get a token from this unit (2787).
Make sure you have Ubuntu 22.04. 3 LTS (no updates) installed
Then open a terminal and run the following commands:
sudo apt-get install node.js
and
sudo apt-get install npm
If you run node --tls-min-v1.0 and you donāt get an error saying thatās an invalid flag, your node version is probably ok.
Install this plugin by running npm install -g homebridge-plugin-samsung-air-conditioner
So I cd`` d into the directory that contains homebridge-samsung-ac-get-token (which i found out using which homebridge-samsung-ac-get-token)
In my case the path was: /usr/local/bin/homebridge-samsung-ac-get-token
Use now: cd /usr/local/bin/
Then use the following command to get the token. Note that AC must be off. In the terminal it will tell you when to turn it on to make the token visible.homebridge-samsung-ac-get-token AC IP ADDRESS --skipCertificate
Then it gives information about port, host, etc. and also indicates the following:
āPower on the device within the next 30 secondsā
So turn on your AC and you will receive your token
Source and credits to:
https://www.reddit.com/r/Hassio/comments/k0jta9/how_i_set_up_two_old_samsung_ac_units_using/
Hi guys.
Anyone playing with the esphome_samsung_ac faced wrong CRCs?
I did not found any hints anywhere ![]()
I have obviously the older nonNASA AC. I managed to connect to F1/F2 and temporarily powering the M5 with USB, not from RS485.
And when I plug the AC into power socket I start getting such lines.
[18:47:38][W][samsung_ac:175]: NonNASA: invalid crc - got 54 but should be 48: 32c800fd0035343200c8fe003634
I parsed the message according to https://github.com/DannyDeGaspari/Samsung-HVAC-buscontrol and it seems to be relatively correct.
Length is fine, first and last byte are correct as well, etc.
But the checksum is really incorrect.
I computer manually and the the CRC required by the ESP is the one I computed as well. But obviously in the communication it is different.
Any ideas where to look, what to debug?
Iāve made a bit easier solution for everybody who has an older model (2878).
I have mocked up a script (with the use of ChatGPT using the js script from one of your sources) to get the token from an old version (2878) AC.
It can be used from a HA terminal like python3 .\ac_2878_get_token.py 192.168.1.100 , just need to include in the same folder the ac14k_m.pem file.
The pem file is here.
For controlling the AC the following integration can be used (you can find it in HACS) once you have the token.
And this is the script to get the Token, just save it as ac_2878_get_token.py.
import socket
import ssl
import logging
import re
import argparse
class SamsungAirconditioner:
def __init__(self, ip, logger):
self.options = {'ip': ip}
self.logger = logger
self.token = None
def get_token(self, callback):
if not callable(callback):
raise ValueError("callback is mandatory for get_token")
# Setup SSL context with TLSv1 and AES256-SHA cipher
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) # Force TLSv1
context.set_ciphers('ALL:@SECLEVEL=0')
# Load the certificate you trust (cert.pem)
context.load_cert_chain(certfile='ac14k_m.pem')
context.check_hostname = False # Disable hostname verification for self-signed certs
context.verify_mode = ssl.CERT_NONE # Optional certificate verification (allow self-signed)
try:
with socket.create_connection((self.options['ip'], 2878)) as raw_socket:
with context.wrap_socket(raw_socket, server_hostname=self.options['ip']) as tls_socket:
self.logger.info('connected', {'ipaddr': self.options['ip'], 'port': 2878, 'tls': True})
# Receive loop to handle messages line-by-line
buffer = ""
while True:
data = tls_socket.recv(1024).decode('utf-8')
if not data:
if not self.token:
callback(Exception('premature eof'))
break
buffer += data
while '\n' in buffer:
line, buffer = buffer.split('\n', 1)
self.logger.debug('read: %s', line.strip())
# Handle specific responses
if line.strip() == 'DRC-1.00':
continue
if line.strip() == '<?xml version="1.0" encoding="utf-8" ?><Update Type="InvalidateAccount"/>':
tls_socket.sendall(b'<Request Type="GetToken" />\r\n')
continue
if line.strip() == '<?xml version="1.0" encoding="utf-8" ?><Response Type="GetToken" Status="Ready"/>':
self.logger.info('waiting for token - turn on the AC in the next 30 seconds')
continue
if line.strip() == '<?xml version="1.0" encoding="utf-8" ?><Response Status="Fail" Type="Authenticate" ErrorCode="301" />':
callback(Exception('Failed authentication'))
return
# Check for token in response
token_match = re.search(r'Token="(.*?)"', line)
if token_match:
self.token = token_match.group(1)
self.logger.info('authenticated')
callback(None, self.token)
return
# Handle status updates
if 'Update Type="Status"' in line:
state_match = re.search(r'Attr ID="(.*?)" Value="(.*?)"', line)
if state_match:
state = {state_match.group(1): state_match.group(2)}
self.logger.info('stateChange', state)
continue
# Handle device state
if 'Response Type="DeviceState" Status="Okay"' in line:
state = {}
attributes = line.split("><")
for attr in attributes:
attr_match = re.search(r'Attr ID="(.*?)" Type=".*?" Value="(.*?)"', attr)
if attr_match:
state[attr_match.group(1)] = attr_match.group(2)
self.logger.info('stateChange', state)
except (socket.error, ssl.SSLError) as e:
if not self.token:
callback(e)
# Set up a basic logger
logger = logging.getLogger('SamsungAirconditioner')
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
# Define the callback function to handle the response from get_token
def token_callback(error, token=None):
if error:
print("Error: {}".format(error))
else:
print("Retrieved token: {}".format(token))
# Set up argument parser for command-line input
parser = argparse.ArgumentParser(description="Samsung Air Conditioner Token Retrieval")
parser.add_argument('ip', type=str, help='IP address of the Samsung Air Conditioner')
args = parser.parse_args()
# Create an instance of the SamsungAirconditioner class with the provided IP
aircon = SamsungAirconditioner(ip=args.ip, logger=logger)
# Call the get_token method, passing the token_callback function
aircon.get_token(token_callback)
I just released a custom component for ESPHome
Hi friends,
I recently migrated my smart home system from OpenHAB to Home Assistant OS.
I managed to get most of my smart devices working as they should with HA, but Iām struggling with my two Samsung Windfree ACās.
In OpenHAB I controlled these ACās with Rest API HTTP commands, which allowed me to control most of the ACās functions (including Quiet mode).
However, in HA I canāt seem to find the way to send the Quiet command.
I am currently using the SmartThings Custom HACS integration to control my ACās, but this integration is not allowing my to set Quiet mode.
If I understand correctly, the Rest API method should allow me to send the Quiet command with HA, but so far, I couldnāt figure out how to do it.
Can anyone, please, assist me in configuring the Rest API commands? ![]()
Hi folks,
I managed to edit viestaās SmartThings Custom HACS integration to add support for Quiet and Fast Turbo modes on Samsung ARTIK051_KRAC_18K OCF Devices.
If you own such ACās you can simply edit the climate.py file (that is located in the /custom_components/smartthings/ folder) with the following code:
"""Support for climate devices through the SmartThings cloud API."""
from __future__ import annotations
import asyncio
from collections.abc import Iterable, Sequence
import logging
from pysmartthings import Attribute, Capability
from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN, ClimateEntity
from homeassistant.components.climate.const import (
ATTR_HVAC_MODE,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
HVACAction,
HVACMode,
ClimateEntityFeature,
)
from homeassistant.const import ATTR_TEMPERATURE
from . import SmartThingsEntity
from .const import DATA_BROKERS, DOMAIN, UNIT_MAP
ATTR_OPERATION_STATE = "operation_state"
MODE_TO_STATE = {
"auto": HVACMode.HEAT_COOL,
"cool": HVACMode.COOL,
"eco": HVACMode.AUTO,
"rush hour": HVACMode.AUTO,
"emergency heat": HVACMode.HEAT,
"heat": HVACMode.HEAT,
"off": HVACMode.OFF,
"wind": HVACMode.FAN_ONLY,
}
STATE_TO_MODE = {
HVACMode.HEAT_COOL: "auto",
HVACMode.COOL: "cool",
HVACMode.HEAT: "heat",
HVACMode.OFF: "off",
HVACMode.FAN_ONLY: "wind",
}
OPERATING_STATE_TO_ACTION = {
"cooling": HVACAction.COOLING,
"fan only": HVACAction.FAN,
"heating": HVACAction.HEATING,
"idle": HVACAction.IDLE,
"pending cool": HVACAction.COOLING,
"pending heat": HVACAction.HEATING,
"vent economizer": HVACAction.FAN,
}
AC_MODE_TO_STATE = {
"auto": HVACMode.HEAT_COOL,
"cool": HVACMode.COOL,
"dry": HVACMode.DRY,
"coolClean": HVACMode.COOL,
"dryClean": HVACMode.DRY,
"heat": HVACMode.HEAT,
"heatClean": HVACMode.HEAT,
"fanOnly": HVACMode.FAN_ONLY,
"wind": HVACMode.FAN_ONLY,
}
STATE_TO_AC_MODE = {
HVACMode.HEAT_COOL: "auto",
HVACMode.COOL: "cool",
HVACMode.DRY: "dry",
HVACMode.HEAT: "heat",
HVACMode.FAN_ONLY: "wind",
}
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add climate entities for a config entry."""
ac_capabilities = [
Capability.air_conditioner_mode,
Capability.air_conditioner_fan_mode,
Capability.switch,
Capability.temperature_measurement,
Capability.thermostat_cooling_setpoint,
]
broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
entities = []
for device in broker.devices.values():
if not broker.any_assigned(device.device_id, CLIMATE_DOMAIN):
continue
if all(capability in device.capabilities for capability in ac_capabilities):
entities.append(SmartThingsAirConditioner(device))
else:
entities.append(SmartThingsThermostat(device))
async_add_entities(entities, True)
def get_capabilities(capabilities: Sequence[str]) -> Sequence[str] | None:
"""Return all capabilities supported if minimum required are present."""
supported = [
Capability.air_conditioner_mode,
Capability.air_conditioner_fan_mode,
"fanOscillationMode",
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,
Capability.execute,
"custom.airConditionerOptionalMode",
"custom.thermostatSetpointControl",
]
# Can have this legacy/deprecated capability
if Capability.thermostat in capabilities:
return supported
# Or must have all of these thermostat capabilities
thermostat_capabilities = [
Capability.temperature_measurement,
Capability.thermostat_cooling_setpoint,
Capability.thermostat_heating_setpoint,
Capability.thermostat_mode,
]
if all(capability in capabilities for capability in thermostat_capabilities):
return supported
# Or must have all of these A/C capabilities
ac_capabilities = [
Capability.air_conditioner_mode,
Capability.air_conditioner_fan_mode,
Capability.switch,
Capability.temperature_measurement,
Capability.thermostat_cooling_setpoint,
]
if all(capability in capabilities for capability in ac_capabilities):
return supported
return None
class SmartThingsThermostat(SmartThingsEntity, ClimateEntity):
"""Define a SmartThings climate entities."""
def __init__(self, device):
"""Init the class."""
super().__init__(device)
self._supported_features = self._determine_features()
self._hvac_mode = None
self._hvac_modes = None
def _determine_features(self):
flags = ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
if self._device.get_capability(
Capability.thermostat_fan_mode, Capability.thermostat
):
flags |= ClimateEntityFeature.FAN_MODE
return flags
async def async_set_fan_mode(self, fan_mode):
"""Set new target fan mode."""
await self._device.set_thermostat_fan_mode(fan_mode, 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_schedule_update_ha_state(True)
async def async_set_hvac_mode(self, hvac_mode):
"""Set new target operation mode."""
mode = STATE_TO_MODE[hvac_mode]
await self._device.set_thermostat_mode(mode, 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_schedule_update_ha_state(True)
async def async_set_temperature(self, **kwargs):
"""Set new operation mode and target temperatures."""
# Operation state
if operation_state := kwargs.get(ATTR_HVAC_MODE):
mode = STATE_TO_MODE[operation_state]
await self._device.set_thermostat_mode(mode, set_status=True)
await self.async_update()
# Heat/cool setpoint
heating_setpoint = None
cooling_setpoint = None
if self.hvac_mode == HVACMode.HEAT:
heating_setpoint = kwargs.get(ATTR_TEMPERATURE)
elif self.hvac_mode == HVACMode.COOL:
cooling_setpoint = kwargs.get(ATTR_TEMPERATURE)
else:
heating_setpoint = kwargs.get(ATTR_TARGET_TEMP_LOW)
cooling_setpoint = kwargs.get(ATTR_TARGET_TEMP_HIGH)
tasks = []
if heating_setpoint is not None:
tasks.append(
self._device.set_heating_setpoint(
round(heating_setpoint, 3), set_status=True
)
)
if cooling_setpoint is not None:
tasks.append(
self._device.set_cooling_setpoint(
round(cooling_setpoint, 3), set_status=True
)
)
await asyncio.gather(*tasks)
# State is set optimistically in the commands above, therefore update
# the entity state ahead of receiving the confirming push updates
self.async_schedule_update_ha_state(True)
async def async_update(self):
"""Update the attributes of the climate device."""
thermostat_mode = self._device.status.thermostat_mode
self._hvac_mode = MODE_TO_STATE.get(thermostat_mode)
if self._hvac_mode is None:
_LOGGER.debug(
"Device %s (%s) returned an invalid hvac mode: %s",
self._device.label,
self._device.device_id,
thermostat_mode,
)
modes = set()
supported_modes = self._device.status.supported_thermostat_modes
if isinstance(supported_modes, Iterable):
for mode in supported_modes:
if (state := MODE_TO_STATE.get(mode)) is not None:
modes.add(state)
else:
_LOGGER.debug(
"Device %s (%s) returned an invalid supported thermostat mode: %s",
self._device.label,
self._device.device_id,
mode,
)
else:
_LOGGER.debug(
"Device %s (%s) returned invalid supported thermostat modes: %s",
self._device.label,
self._device.device_id,
supported_modes,
)
self._hvac_modes = list(modes)
@property
def current_humidity(self):
"""Return the current humidity."""
return self._device.status.humidity
@property
def current_temperature(self):
"""Return the current temperature."""
return self._device.status.temperature
@property
def fan_mode(self):
"""Return the fan setting."""
return self._device.status.thermostat_fan_mode
@property
def fan_modes(self):
"""Return the list of available fan modes."""
return self._device.status.supported_thermostat_fan_modes
@property
def hvac_action(self) -> str | None:
"""Return the current running hvac operation if supported."""
return OPERATING_STATE_TO_ACTION.get(
self._device.status.thermostat_operating_state
)
@property
def hvac_mode(self):
"""Return current operation ie. heat, cool, idle."""
return self._hvac_mode
@property
def hvac_modes(self):
"""Return the list of available operation modes."""
return self._hvac_modes
@property
def supported_features(self):
"""Return the supported features."""
return self._supported_features
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
if self.hvac_mode == HVACMode.COOL:
return self._device.status.cooling_setpoint
if self.hvac_mode == HVACMode.HEAT:
return self._device.status.heating_setpoint
return None
@property
def target_temperature_high(self):
"""Return the highbound target temperature we try to reach."""
if self.hvac_mode == HVACMode.HEAT_COOL:
return self._device.status.cooling_setpoint
return None
@property
def target_temperature_low(self):
"""Return the lowbound target temperature we try to reach."""
if self.hvac_mode == HVACMode.HEAT_COOL:
return self._device.status.heating_setpoint
return None
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return UNIT_MAP.get(self._device.status.attributes[Attribute.temperature].unit)
class SmartThingsAirConditioner(SmartThingsEntity, ClimateEntity):
"""Define a SmartThings Air Conditioner."""
is_faulty_quiet = False
def __init__(self, device):
"""Init the class."""
super().__init__(device)
self._hvac_modes = None
async def async_set_fan_mode(self, fan_mode):
"""Set new target fan mode."""
await self._device.set_fan_mode(fan_mode, 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_set_preset_mode(self, preset_mode):
"""Set new target fan mode."""
# if self.is_faulty_quiet and preset_mode == "quiet":
# result = await self._device.execute(
# "mode/convenient/vs/0", {"x.com.samsung.da.modes": "Quiet"}
# )
# else:
result = await self._device.command(
"main",
"custom.airConditionerOptionalMode",
"setAcOptionalMode",
[preset_mode],
)
if result:
self._device.status.update_attribute_value("acOptionalMode", preset_mode)
self.async_write_ha_state()
async def async_set_swing_mode(self, swing_mode):
"""Set new target swing mode."""
# await self._device.set_fan_oscillation_mode(swing_mode, set_status=True)
result = await self._device.command(
"main",
"fanOscillationMode",
"setFanOscillationMode",
[swing_mode],
)
# State is set optimistically in the command above, therefore update
# the entity state ahead of receiving the confirming push updates
if result:
self._device.status.update_attribute_value("fanOscillationMode", swing_mode)
self.async_write_ha_state()
async def async_set_hvac_mode(self, hvac_mode):
"""Set new target operation mode."""
if hvac_mode == HVACMode.OFF:
await self.async_turn_off()
return
tasks = []
# Turn on the device if it's off before setting mode.
if not self._device.status.switch:
tasks.append(self._device.switch_on(set_status=True))
tasks.append(
self._device.set_air_conditioner_mode(
STATE_TO_AC_MODE[hvac_mode], set_status=True
)
)
await asyncio.gather(*tasks)
# 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_set_temperature(self, **kwargs):
"""Set new target temperature."""
tasks = []
# operation mode
if operation_mode := kwargs.get(ATTR_HVAC_MODE):
if operation_mode == HVACMode.OFF:
tasks.append(self._device.switch_off(set_status=True))
else:
if not self._device.status.switch:
tasks.append(self._device.switch_on(set_status=True))
tasks.append(self.async_set_hvac_mode(operation_mode))
# temperature
tasks.append(
self._device.set_cooling_setpoint(kwargs[ATTR_TEMPERATURE], set_status=True)
)
await asyncio.gather(*tasks)
# 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):
"""Turn device on."""
await self._device.switch_on(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_off(self):
"""Turn device off."""
await self._device.switch_off(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_update(self):
"""Update the calculated fields of the AC."""
modes = {HVACMode.OFF}
for mode in self._device.status.supported_ac_modes:
if (state := AC_MODE_TO_STATE.get(mode)) is not None:
modes.add(state)
else:
_LOGGER.debug(
"Device %s (%s) returned an invalid supported AC mode: %s",
self._device.label,
self._device.device_id,
mode,
)
self._hvac_modes = list(modes)
@property
def current_humidity(self):
"""Return the current humidity."""
return self._device.status.humidity
@property
def current_temperature(self):
"""Return the current temperature."""
return self._device.status.temperature
@property
def extra_state_attributes(self):
"""
Return device specific state attributes.
"""
attributes = []
custom_attributes = []
state_attributes = {}
for attribute in attributes:
value = getattr(self._device.status, attribute)
if value is not None:
state_attributes[attribute] = value
for attribute in custom_attributes:
value = self._device.status.attributes[attribute].value
if value is not None:
state_attributes[attribute] = value
return state_attributes
@property
def fan_mode(self):
"""Return the fan setting."""
return self._device.status.fan_mode
@property
def fan_modes(self):
"""Return the list of available fan modes."""
return self._device.status.supported_ac_fan_modes
@property
def swing_mode(self):
"""Return the swing setting."""
return self._device.status.attributes["fanOscillationMode"].value
@property
def swing_modes(self):
"""Give all swing modes, if attribute is found it most likely works. Samsung gives null, work-around"""
if (
self._device.status.attributes["supportedFanOscillationModes"].value
is not None
):
fan_oscillation_modes = [
str(x)
for x in self._device.status.attributes[
"supportedFanOscillationModes"
].value
]
return fan_oscillation_modes
elif self._device.status.attributes["fanOscillationMode"].value is not None:
return ["fixed", "all", "vertical", "horizontal"]
else:
return None
@property
def preset_mode(self):
"""Return the ac optional mode setting."""
return self._device.status.attributes["acOptionalMode"].value
@property
def preset_modes(self):
"""Return the list of available ac optional modes, in samsung case check that windfree cannot be selected when in heating."""
restricted_values = ["windFree"]
model = self._device.status.attributes[Attribute.mnmo].value.split("|")[0]
supported_ac_optional_modes = [
str(x)
for x in self._device.status.attributes["supportedAcOptionalMode"].value
]
if "quiet" not in supported_ac_optional_modes and model == "ARTIK051_KRAC_18K":
supported_ac_optional_modes.append("quiet")
self.is_faulty_quiet = True
if "speed" not in supported_ac_optional_modes and model == "ARTIK051_KRAC_18K":
supported_ac_optional_modes.append("speed")
self.is_faulty_speed = True
if self._device.status.air_conditioner_mode in ("auto", "heat"):
if any(
restrictedvalue in supported_ac_optional_modes
for restrictedvalue in restricted_values
):
reduced_supported_optional_modes = supported_ac_optional_modes
reduced_supported_optional_modes.remove("windFree")
return reduced_supported_optional_modes
else:
return supported_ac_optional_modes
@property
def hvac_mode(self):
"""Return current operation ie. heat, cool, idle."""
if not self._device.status.switch:
return HVACMode.OFF
return AC_MODE_TO_STATE.get(self._device.status.air_conditioner_mode)
@property
def hvac_modes(self):
"""Return the list of available operation modes."""
return self._hvac_modes
@property
def supported_features(self):
"""Return the supported features."""
supported_ac_optional_modes = [
str(x)
for x in self._device.status.attributes["supportedAcOptionalMode"].value
]
if len(supported_ac_optional_modes) == 1 and supported_ac_optional_modes[0] == "off":
return (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.SWING_MODE
)
return (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.SWING_MODE
| ClimateEntityFeature.PRESET_MODE
)
@property
def max_temp(self):
"""Return the maximum temperature limit"""
return int(self._device.status.attributes["maximumSetpoint"].value)
@property
def min_temp(self):
"""Return the minimum temperature limit"""
return int(self._device.status.attributes["minimumSetpoint"].value)
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self._device.status.cooling_setpoint
@property
def target_temperature_step(self):
"""set the target temperature step size"""
return 1.0
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return UNIT_MAP.get(self._device.status.attributes[Attribute.temperature].unit)
I hope this helps someone else.
glad that you managed to get it to work.
But why did you use a HACS integration instead of the things that are already provided by HA?
If you say that you could to rest api calls, why not just use
RESTful Command - Home Assistant ?
The HACS integration currently offers more functionality compared to the HA SmartThings integration.
For example, with the SmartThings Custom HACS integration you can disable the Beep sounds of the ACās. The HA SmartThings integration doesnāt support disabling the beep yet.
Regarding why I didnāt use RESTful Command, Iām pretty new to HA and I still donāt know how to configure this method. I tried and failed a few times.
but work also qith samsung AC with 2878 comunication port?
Yes, this custom component works with the old Samsung ACs with the 2878 communication port.
You can use the script from my previous post to get the token.
Hi there, I am trying to get the token using your script.
HAOS is installed in an old laptop.
I am using a desktop PC with win10 (no linux).
I saved the script as .\ac_2878_get_token.py in the custom_components\climate_ip folder of the HAOS system using smb.
when I login into the SSH and try to execute the script I got this error:
python3 .\ac_2878_get_token.py 192.168.0.247
Do I have to mode the script to a different location?
I must have installed python there, to be honest, I donāt know. I thought that HAOS has the python environment installed by default, but donāt worry, the HA container has.
docker exec -it homeassistant /bin/bash
to enter the HA container, and then go to the same directory, and execute the command like you tried, but this is Linux so it will ./ac_2878_get_token.py
Once you executed the script you will need to turn on the AC with the remote, but I think it will give you the instructions. I cannot remember, sorry.
When the AC turns on it will give you the required token and you will be able to set up the integration through the configuration.yaml.
Donāt expect miracles from the AC. The response is quite slow time to time, but generally working.
Let me know if you have any other issues.
Thanks.
The code suggested (docker exec -it homeassistant /bin/bash) gave this output:
bash: docker: command not found
How are you running your HA then? Because that should work without any issues if you have HAOS, and you are accessing it through the SSH addon.
You need a python3 environment to run the script. It is provided inside the HA container, but I have no clue how is that docker command not working for you.
As per this comment, you likely need some further settings to access the container.
Read the comment and the following few others how to access the container. It will get you there.
Use this addon for SSH.
And disable the protection mode.
The page explains how to set it up.
Any success? I know it is a life saver this integration as the original app is not working anymore, unless you have a really old phone to run it. Otherwise you are stuck with the remote or an IR Blaster, but then you donāt have any feedback of the values.
Dear all, I managed to get the token! thanks for the support.
Now is time to configure climate ip.
edit:
I managed to create the new clima-samsung entity, but looks like itās not working correctly (yet).
The clima is not sending info about the temperature, or simply siwtching on/off.
climate:
- platform: climate_ip
config_file: /config/custom_components/climate_ip/samsung_2878.yaml
ip_address: 192.168.0.xxx
token: SDADSAGFDxxxxxxxx
cert: /config/custom_components/climate_ip/ac14k_m.pem
mac: 54-xx-xx-xx-xx-xx
name: 'clima_samsung'
poll: True
This is how it works for me:
climate:
- platform: climate_ip
config_file: '/config/custom_components/climate_ip/samsung_2878.yaml'
ip_address: '192.168.0.xxx'
token: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
cert: 'ac14k_m.pem'
mac: 'put the mac address in bc:8c:cd:.... format'
name: 'AC Living Room'
poll: True
If it is not working with this setup, then look at the logs. And search for climate_ip. You should see an error message somewhere.
A little progress: seeing temperature settings.
Still not working: unable to send any command.
I opened an issue in github, but at the moment I have no clue on what I can change in terms of configuration files to make it work.
climate:
- platform: climate_ip
config_file: '/config/custom_components/climate_ip/samsung_2878.yaml'
ip_address: '192.168.0.247'
token: 'x-x-x-x-x'
cert: '/config/custom_components/climate_ip/ac14k_m.pem'
mac: 'x-x-x-x-x-x'
name: 'clima_samsung'
poll: True
Installation method Home Assistant OS
Core 2025.7.1
Supervisor 2025.07.1
Operating System 16.0
Frontend 20250702.1
Logger: homeassistant.util.loop
Source: util/loop.py:137
First occurred: July 12, 2025 at 7:39:03 PM (2 occurrences)
Last logged: July 12, 2025 at 7:39:03 PM
Detected blocking call to load_verify_locations with args (<ssl.SSLContext object at 0x7f1b84c29400>,) inside the event loop by custom integration 'climate_ip' at custom_components/climate_ip/samsung_2878.py, line 266: sslContext.load_verify_locations(cafile=cfg.cert) (offender: /config/custom_components/climate_ip/samsung_2878.py, line 266: sslContext.load_verify_locations(cafile=cfg.cert)), please create a bug report at https://github.com/atxbyea/samsungrac/issues For developers, please see https://developers.home-assistant.io/docs/asyncio_blocking_operations/#load_verify_locations Traceback (most recent call last): File "", line 198, in _run_module_as_main File "", line 88, in _run_code File "/usr/src/homeassistant/homeassistant/main.py", line 223, in sys.exit(main()) File "/usr/src/homeassistant/homeassistant/main.py", line 209, in main exit_code = runner.run(runtime_conf) File "/usr/src/homeassistant/homeassistant/runner.py", line 154, in run return loop.run_until_complete(setup_and_run_hass(runtime_config)) File "/usr/local/lib/python3.13/asyncio/base_events.py", line 706, in run_until_complete self.run_forever() File "/usr/local/lib/python3.13/asyncio/base_events.py", line 677, in run_forever self._run_once() File "/usr/local/lib/python3.13/asyncio/base_events.py", line 2034, in _run_once handle._run() File "/usr/local/lib/python3.13/asyncio/events.py", line 89, in _run self._context.run(self._callback, *self._args) File "/config/custom_components/climate_ip/climate.py", line 126, in async_setup_platform device_controller = await create_controller( File "/config/custom_components/climate_ip/controller.py", line 71, in create_controller if await c.initialize(): File "/config/custom_components/climate_ip/controller_yaml.py", line 177, in initialize self.update_state() File "/config/custom_components/climate_ip/controller_yaml.py", line 229, in update_state self._state_getter.update_state(self._state_getter.value, debug) File "/config/custom_components/climate_ip/properties.py", line 254, in update_state device_state = self.get_connection(None).execute( File "/config/custom_components/climate_ip/samsung_2878.py", line 323, in execute self.send_socket_command(message, 1) File "/config/custom_components/climate_ip/samsung_2878.py", line 223, in send_socket_command sslSocket = self.socket File "/config/custom_components/climate_ip/samsung_2878.py", line 296, in socket self.create_connection() File "/config/custom_components/climate_ip/samsung_2878.py", line 266, in create_connection sslContext.load_verify_locations(cafile=cfg.cert)
Detected blocking call to load_cert_chain with args (<ssl.SSLContext object at 0x7f1b84c29400>, '/config/custom_components/climate_ip/ac14k_m.pem') inside the event loop by custom integration 'climate_ip' at custom_components/climate_ip/samsung_2878.py, line 268: sslContext.load_cert_chain(cfg.cert) (offender: /config/custom_components/climate_ip/samsung_2878.py, line 268: sslContext.load_cert_chain(cfg.cert)), please create a bug report at https://github.com/atxbyea/samsungrac/issues For developers, please see https://developers.home-assistant.io/docs/asyncio_blocking_operations/#load_cert_chain Traceback (most recent call last): File "", line 198, in _run_module_as_main File "", line 88, in _run_code File "/usr/src/homeassistant/homeassistant/main.py", line 223, in sys.exit(main()) File "/usr/src/homeassistant/homeassistant/main.py", line 209, in main exit_code = runner.run(runtime_conf) File "/usr/src/homeassistant/homeassistant/runner.py", line 154, in run return loop.run_until_complete(setup_and_run_hass(runtime_config)) File "/usr/local/lib/python3.13/asyncio/base_events.py", line 706, in run_until_complete self.run_forever() File "/usr/local/lib/python3.13/asyncio/base_events.py", line 677, in run_forever self._run_once() File "/usr/local/lib/python3.13/asyncio/base_events.py", line 2034, in _run_once handle._run() File "/usr/local/lib/python3.13/asyncio/events.py", line 89, in _run self._context.run(self._callback, *self._args) File "/config/custom_components/climate_ip/climate.py", line 126, in async_setup_platform device_controller = await create_controller( File "/config/custom_components/climate_ip/controller.py", line 71, in create_controller if await c.initialize(): File "/config/custom_components/climate_ip/controller_yaml.py", line 177, in initialize self.update_state() File "/config/custom_components/climate_ip/controller_yaml.py", line 229, in update_state self._state_getter.update_state(self._state_getter.value, debug) File "/config/custom_components/climate_ip/properties.py", line 254, in update_state device_state = self.get_connection(None).execute( File "/config/custom_components/climate_ip/samsung_2878.py", line 323, in execute self.send_socket_command(message, 1) File "/config/custom_components/climate_ip/samsung_2878.py", line 223, in send_socket_command sslSocket = self.socket File "/config/custom_components/climate_ip/samsung_2878.py", line 296, in socket self.create_connection() File "/config/custom_components/climate_ip/samsung_2878.py", line 268, in create_connection sslContext.load_cert_chain(cfg.cert)`


