Samsung Air Conditioner

For anyone using the old 8888 or 2878 devices, I’m in the process of writing a native HA integration, complete with config flow, automatic token fetching and power usage reporting (where applicable). I already have something that I use, but it only works for 8888 devices for now. climate_ip is good, but it’s been written some time ago, isn’t really being maintained anymore and doesn’t work with async properly

4 Likes

I have an old machine working on port 2878, not able to get the token. Woul love seeing your app.

Hi,
I just got 3 new samsung Windfree units installed AR07TXF… I have been fighting to get the access token but so far I failed. It seems they don’t work like the other models. I have connected them with Smarthings and they work fine but I would like to have them on my hass dshboard. What you have done is exactly what I am willing to do.
If you don’t mind sharing what you have done that would be great.

Thanks

Hi JRFabbi,
Can you share? how did you get all that, i have the same module can you help?
thanks

Just got a windfree samsung, I want to have the card, thanks!

Hello!
Unfortunately someone has broken into my house last saturday and now I’m moving to another house.

You guys that have windfree modules can access SmartThings integration on HASS? (this is a prerequisite to get it working because there is no direct conection on those units)

I will try to make a post with everything you have to change on climate.py (from SmartThings integration) and device.py (on pysmartthings that is used in SmartThings HASS integration) to get all custom options running.

I’ve created this post on SmartThings community (https://community.smartthings.com/t/airflowdirection-status-dead/198100/15) that describes how-to use OCF capabilities from our devices (to change wind direction, set lights and buzzer and all other options), for those options I’m using REST Command to do the job.

This is my REST Commands

ac_clean_on:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "custom.autoCleaningMode",  "command": "setAutoCleaningMode",  "arguments": [ "on" ]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_clean_off:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "custom.autoCleaningMode",  "command": "setAutoCleaningMode",  "arguments": [ "off" ]}  ]}'
  content_type:  'application/json; charset=utf-8'

ac_light_off:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Autoclean_Off","Light_On"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_light_on:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Light_Off"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_volume_mute:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Volume_Mute"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_volume_normal:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Volume_100"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_purifier:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Spi_On"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_purifier_off:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Spi_Off"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_comfort:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Comode_Comfort"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_2step:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Comode_2Step"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_speed:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Comode_Speed"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_special_off:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["mode/vs/0",{"x.com.samsung.da.options": ["Comode_Off"]}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_flow_all:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["airflow/vs/0",{"x.com.samsung.da.direction": "All"}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_flow_up_low:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["airflow/vs/0",{"x.com.samsung.da.direction": "Up_And_Low"}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_flow_left_right:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["airflow/vs/0",{"x.com.samsung.da.direction": "Left_And_Right"}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_flow_fix:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "execute",  "command": "execute",  "arguments": ["airflow/vs/0",{"x.com.samsung.da.direction": "Fix"}]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_windfree_on:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "custom.airConditionerOptionalMode",  "command": "setAcOptionalMode",  "arguments": [ "windFree" ]}  ]}'
  content_type:  'application/json; charset=utf-8'
ac_windfree_off:
  url: https://api.smartthings.com/v1/devices/{{device_id}}/commands
  method: POST
  headers:
    authorization: !secret rest_smartthings_secret
    accept: 'application/json'
    user-agent: 'Home-Assistant'
  payload: '{  "commands": [{  "component": "main",  "capability": "custom.airConditionerOptionalMode",  "command": "setAcOptionalMode",  "arguments": [ "off" ]}  ]}'
  content_type:  'application/json; charset=utf-8'

And I’m using switches and sensors to command those options, I will create a post with all the changes to get it working.

This is one of my switches

- platform: template
  switches:
    acsuite_windfree_on:
      value_template: "{{ is_state('sensor.acsuite_optmode', 'WindFree') }}"
      turn_on:
        service: rest_command.ac_windfree_on
        data:
          device_id: "xxxxxxxxxxxx"
      turn_off:
        service: rest_command.ac_windfree_off
        data:
          device_id: "xxxxxxxxxxxx"

On device_id you have to put your SmartThings device ID, you can get this using a simple GET on SmartThings API.

4 Likes

Yes, now unavailable though, not sure why

Try to remove you SmartThings integration and reconfigure again.

Before you configure the integration put ALL air-conditioners to Cold mode (the device MUST be not on Wind mode because it can register incorrectly no HASS, not as Climate)

Thanks JRFabbi for your reply.
Looking forward to your post on what to change.
Fighting with something else in the meantime. The never ending story of getting things in HASS.
Good luck with your house move and hope things will get better.

Seb

Thanks JRFabbi,
Are you also able to colect the data from powerConsumptionReport on smarthings ?

I think you can get using power_consumption attributes (you have the power consumption and you combine it using the time that device is on) but I don’t know how to calculate this (I don’t know what is the unit of measure returned by this attribute)

Hello all!

My unit is disassembled (I’m moving to another home) so I can’t help with tests but I can show everything I changed to make this working.

First you need to change “capability.py” on pysmartthings (it’s on site-packages folder, something linke “/usr/local/lib/python3.7/site-packages/pysmartthings/” and add those custom capabilities:

custom.spiMode
custom.autoCleaningMode
custom.airConditionerOptionalMode

If you don’t know how to add this, here is my file

"""
Defines SmartThings capabilities and attributes.

https://smartthings.developer.samsung.com/docs/api-ref/capabilities.html
"""

CAPABILITIES_TO_ATTRIBUTES = {
    "accelerationSensor": ["acceleration"],
    "activityLightingMode": ["lightingMode"],
    "airConditionerFanMode": ["fanMode", "supportedAcFanModes"],
    "airConditionerMode": ["airConditionerMode", "supportedAcModes"],
    "airFlowDirection": ["airFlowDirection"],
    "airQualitySensor": ["airQuality"],
    "alarm": ["alarm"],
    "audioMute": ["mute"],
    "audioVolume": ["volume"],
    "battery": ["battery"],
    "bodyMassIndexMeasurement": ["bmiMeasurement"],
    "bodyWeightMeasurement": ["bodyWeightMeasurement"],
    "button": ["button", "numberOfButtons", "supportedButtonValues"],
    "carbonDioxideMeasurement": ["carbonDioxide"],
    "carbonMonoxideDetector": ["carbonMonoxide"],
    "carbonMonoxideMeasurement": ["carbonMonoxideLevel"],
    "colorControl": ["color", "hue", "saturation"],
    "colorTemperature": ["colorTemperature"],
    "contactSensor": ["contact"],
    "demandResponseLoadControl": ["drlcStatus"],
    "dishwasherMode": ["dishwasherMode"],
    "dishwasherOperatingState": [
        "machineState",
        "supportedMachineStates",
        "dishwasherJobState",
        "completionTime",
    ],
    "doorControl": ["door"],
    "dryerMode": ["dryerMode"],
    "dryerOperatingState": [
        "machineState",
        "supportedMachineStates",
        "dryerJobState",
        "completionTime",
    ],
    "dustSensor": ["fineDustLevel", "dustLevel"],
    "energyMeter": ["energy"],
    "equivalentCarbonDioxideMeasurement": ["equivalentCarbonDioxideMeasurement"],
    "execute": ["data"],
    "fanSpeed": ["fanSpeed"],
    "custom.spiMode": ["spiMode"],
    "custom.autoCleaningMode": ["autoCleaningMode"],
    "custom.airConditionerOptionalMode": ["acOptionalMode"],
    "filterStatus": ["filterStatus"],
    "formaldehydeMeasurement": ["formaldehydeLevel"],
    "garageDoorControl": ["door"],
    "illuminanceMeasurement": ["illuminance"],
    "infraredLevel": ["infraredLevel"],
    "lock": ["lock"],
    "mediaInputSource": ["inputSource", "supportedInputSources"],
    "mediaPlaybackRepeat": ["playbackRepeatMode"],
    "mediaPlaybackShuffle": ["playbackShuffle"],
    "mediaPlayback": ["playbackStatus", "supportedPlaybackCommands"],
    "motionSensor": ["motion"],
    "ocf": [
        "st",
        "mnfv",
        "mndt",
        "mnhw",
        "di",
        "mnsl",
        "dmv",
        "n",
        "vid",
        "mnmo",
        "mnmn",
        "mnml",
        "mnpv",
        "mnos",
        "pi",
        "icv",
    ],
    "odorSensor": ["odorLevel"],
    "ovenMode": ["ovenMode"],
    "ovenOperatingState": [
        "machineState",
        "supportedMachineStates",
        "ovenJobState",
        "completionTime",
        "operationTime",
        "progress",
    ],
    "ovenSetpoint": ["ovenSetpoint"],
    "powerConsumptionReport": ["powerConsumption"],
    "powerMeter": ["power"],
    "powerSource": ["powerSource"],
    "presenceSensor": ["presence"],
    "rapidCooling": ["rapidCooling"],
    "refrigerationSetpoint": ["refrigerationSetpoint"],
    "relativeHumidityMeasurement": ["humidity"],
    "robotCleanerCleaningMode": ["robotCleanerCleaningMode"],
    "robotCleanerMovement": ["robotCleanerMovement"],
    "robotCleanerTurboMode": ["robotCleanerTurboMode"],
    "signalStrength": ["lqi", "rssi"],
    "smokeDetector": ["smoke"],
    "soundSensor": ["sound"],
    "switchLevel": ["level"],
    "switch": ["switch"],
    "tamperAlert": ["tamper"],
    "temperatureMeasurement": ["temperature"],
    "thermostat": [
        "coolingSetpoint",
        "coolingSetpointRange",
        "heatingSetpoint",
        "heatingSetpointRange",
        "schedule",
        "temperature",
        "thermostatFanMode",
        "supportedThermostatFanModes",
        "thermostatMode",
        "supportedThermostatModes",
        "thermostatOperatingState",
        "thermostatSetpoint",
        "thermostatSetpointRange",
    ],
    "thermostatCoolingSetpoint": ["coolingSetpoint"],
    "thermostatFanMode": ["thermostatFanMode", "supportedThermostatFanModes"],
    "thermostatHeatingSetpoint": ["heatingSetpoint"],
    "thermostatMode": ["thermostatMode", "supportedThermostatModes"],
    "thermostatOperatingState": ["thermostatOperatingState"],
    "thermostatSetpoint": ["thermostatSetpoint"],
    "threeAxis": ["threeAxis"],
    "tvChannel": ["tvChannel"],
    "tvocMeasurement": ["tvocLevel"],
    "ultravioletIndex": ["ultravioletIndex"],
    "valve": ["valve"],
    "voltageMeasurement": ["voltage"],
    "washerMode": ["washerMode"],
    "washerOperatingState": [
        "machineState",
        "supportedMachineStates",
        "washerJobState",
        "completionTime",
    ],
    "waterSensor": ["water"],
    "windowShade": ["windowShade"],
}
CAPABILITIES = list(CAPABILITIES_TO_ATTRIBUTES)
ATTRIBUTES = {
    attrib
    for attributes in CAPABILITIES_TO_ATTRIBUTES.values()
    for attrib in attributes
}


class Capability:
    """Define common capabilities."""

    acceleration_sensor = "accelerationSensor"
    activity_lighting_mode = "activityLightingMode"
    air_conditioner_fan_mode = "airConditionerFanMode"
    air_conditioner_mode = "airConditionerMode"
    air_flow_direction = "airFlowDirection"
    air_quality_sensor = "airQualitySensor"
    alarm = "alarm"
    audio_mute = "audioMute"
    audio_volume = "audioVolume"
    battery = "battery"
    body_mass_index_measurement = "bodyMassIndexMeasurement"
    body_weight_measurement = "bodyWeightMeasurement"
    button = "button"
    carbon_dioxide_measurement = "carbonDioxideMeasurement"
    carbon_monoxide_detector = "carbonMonoxideDetector"
    carbon_monoxide_measurement = "carbonMonoxideMeasurement"
    color_control = "colorControl"
    color_temperature = "colorTemperature"
    contact_sensor = "contactSensor"
    demand_response_load_control = "demandResponseLoadControl"
    dishwasher_mode = "dishwasherMode"
    dishwasher_operating_state = "dishwasherOperatingState"
    door_control = "doorControl"
    dryer_mode = "dryerMode"
    dryer_operating_state = "dryerOperatingState"
    dust_sensor = "dustSensor"
    energy_meter = "energyMeter"
    equivalent_carbon_dioxide_measurement = "equivalentCarbonDioxideMeasurement"
    execute = "execute"
    fan_speed = "fanSpeed"
    spi_mode = "custom.spiMode"
    autoclean_mode = "custom.autoCleaningMode"
    ac_optional_mode = "custom.airConditionerOptionalMode"
    filter_status = "filterStatus"
    formaldehyde_measurement = "formaldehydeMeasurement"
    garage_door_control = "garageDoorControl"
    illuminance_measurement = "illuminanceMeasurement"
    infrared_level = "infraredLevel"
    lock = "lock"
    media_input_source = "mediaInputSource"
    media_playback = "mediaPlayback"
    media_playback_repeat = "mediaPlaybackRepeat"
    media_playback_shuffle = "mediaPlaybackShuffle"
    motion_sensor = "motionSensor"
    ocf = "ocf"
    odor_sensor = "odorSensor"
    oven_mode = "ovenMode"
    oven_operating_state = "ovenOperatingState"
    oven_setpoint = "ovenSetpoint"
    power_consumption_report = "powerConsumptionReport"
    power_meter = "powerMeter"
    power_source = "powerSource"
    presence_sensor = "presenceSensor"
    rapid_cooling = "rapidCooling"
    refrigeration_setpoint = "refrigerationSetpoint"
    relative_humidity_measurement = "relativeHumidityMeasurement"
    robot_cleaner_cleaning_mode = "robotCleanerCleaningMode"
    robot_cleaner_movement = "robotCleanerMovement"
    robot_cleaner_turbo_mode = "robotCleanerTurboMode"
    signal_strength = "signalStrength"
    smoke_detector = "smokeDetector"
    sound_sensor = "soundSensor"
    switch = "switch"
    switch_level = "switchLevel"
    tamper_alert = "tamperAlert"
    temperature_measurement = "temperatureMeasurement"
    thermostat = "thermostat"
    thermostat_cooling_setpoint = "thermostatCoolingSetpoint"
    thermostat_fan_mode = "thermostatFanMode"
    thermostat_heating_setpoint = "thermostatHeatingSetpoint"
    thermostat_mode = "thermostatMode"
    thermostat_operating_state = "thermostatOperatingState"
    thermostat_setpoint = "thermostatSetpoint"
    three_axis = "threeAxis"
    tv_channel = "tvChannel"
    tvoc_measurement = "tvocMeasurement"
    ultraviolet_index = "ultravioletIndex"
    valve = "valve"
    voltage_measurement = "voltageMeasurement"
    washer_mode = "washerMode"
    washer_operating_state = "washerOperatingState"
    water_sensor = "waterSensor"
    window_shade = "windowShade"


class Attribute:
    """Define common attributes."""

    acceleration = "acceleration"
    air_conditioner_mode = "airConditionerMode"
    air_flow_direction = "airFlowDirection"
    air_quality = "airQuality"
    alarm = "alarm"
    battery = "battery"
    bmi_measurement = "bmiMeasurement"
    body_weight_measurement = "bodyWeightMeasurement"
    button = "button"
    carbon_dioxide = "carbonDioxide"
    carbon_monoxide = "carbonMonoxide"
    carbon_monoxide_level = "carbonMonoxideLevel"
    color = "color"
    color_temperature = "colorTemperature"
    completion_time = "completionTime"
    contact = "contact"
    cooling_setpoint = "coolingSetpoint"
    cooling_setpoint_range = "coolingSetpointRange"
    data = "data"
    di = "di"
    dishwasher_job_state = "dishwasherJobState"
    dishwasher_mode = "dishwasherMode"
    dmv = "dmv"
    door = "door"
    drlc_status = "drlcStatus"
    dryer_job_state = "dryerJobState"
    dryer_mode = "dryerMode"
    dust_level = "dustLevel"
    energy = "energy"
    equivalent_carbon_dioxide_measurement = "equivalentCarbonDioxideMeasurement"
    samsung_options = "x.com.samsung.da.options"
    fan_mode = "fanMode"
    spi_mode = "spiMode"
    autoclean_mode = "autoCleaningMode"
    ac_optional_mode = "acOptionalMode"
    fan_speed = "fanSpeed"
    filter_status = "filterStatus"
    fine_dust_level = "fineDustLevel"
    formaldehyde_level = "formaldehydeLevel"
    heating_setpoint = "heatingSetpoint"
    heating_setpoint_range = "heatingSetpointRange"
    hue = "hue"
    humidity = "humidity"
    icv = "icv"
    illuminance = "illuminance"
    infrared_level = "infraredLevel"
    input_source = "inputSource"
    level = "level"
    lighting_mode = "lightingMode"
    lock = "lock"
    lqi = "lqi"
    machine_state = "machineState"
    mndt = "mndt"
    mnfv = "mnfv"
    mnhw = "mnhw"
    mnml = "mnml"
    mnmn = "mnmn"
    mnmo = "mnmo"
    mnos = "mnos"
    mnpv = "mnpv"
    mnsl = "mnsl"
    motion = "motion"
    mute = "mute"
    n = "n"
    number_of_buttons = "numberOfButtons"
    odor_level = "odorLevel"
    operation_time = "operationTime"
    oven_job_state = "ovenJobState"
    oven_mode = "ovenMode"
    oven_setpoint = "ovenSetpoint"
    pi = "pi"
    playback_repeat_mode = "playbackRepeatMode"
    playback_shuffle = "playbackShuffle"
    playback_status = "playbackStatus"
    power = "power"
    power_consumption = "powerConsumption"
    power_source = "powerSource"
    presence = "presence"
    progress = "progress"
    rapid_cooling = "rapidCooling"
    refrigeration_setpoint = "refrigerationSetpoint"
    robot_cleaner_cleaning_mode = "robotCleanerCleaningMode"
    robot_cleaner_movement = "robotCleanerMovement"
    robot_cleaner_turbo_mode = "robotCleanerTurboMode"
    rssi = "rssi"
    saturation = "saturation"
    schedule = "schedule"
    smoke = "smoke"
    sound = "sound"
    st = "st"
    supported_ac_fan_modes = "supportedAcFanModes"
    supported_ac_modes = "supportedAcModes"
    supported_button_values = "supportedButtonValues"
    supported_input_sources = "supportedInputSources"
    supported_machine_states = "supportedMachineStates"
    supported_playback_commands = "supportedPlaybackCommands"
    supported_thermostat_fan_modes = "supportedThermostatFanModes"
    supported_thermostat_modes = "supportedThermostatModes"
    switch = "switch"
    tamper = "tamper"
    temperature = "temperature"
    thermostat_fan_mode = "thermostatFanMode"
    thermostat_mode = "thermostatMode"
    thermostat_operating_state = "thermostatOperatingState"
    thermostat_setpoint = "thermostatSetpoint"
    thermostat_setpoint_range = "thermostatSetpointRange"
    three_axis = "threeAxis"
    tv_channel = "tvChannel"
    tvoc_level = "tvocLevel"
    ultraviolet_index = "ultravioletIndex"
    valve = "valve"
    vid = "vid"
    voltage = "voltage"
    volume = "volume"
    washer_job_state = "washerJobState"
    washer_mode = "washerMode"
    water = "water"
    window_shade = "windowShade"


ATTRIBUTE_ON_VALUES = {
    Attribute.acceleration: "active",
    Attribute.contact: "open",
    Attribute.filter_status: "replace",
    Attribute.motion: "active",
    Attribute.mute: "muted",
    Attribute.presence: "present",
    Attribute.sound: "detected",
    Attribute.switch: "on",
    Attribute.tamper: "detected",
    Attribute.valve: "open",
    Attribute.water: "wet",
}

You also have to add those capabilities to sensors on smartthings integration, the best way to do is by downloading latest version on repository (here) and manually create smartthings on custom_components, after you edit sensors and climate files:

My sensor.py

"""Support for sensors through the SmartThings cloud API."""
from collections import namedtuple
from typing import Optional, Sequence

from pysmartthings import Attribute, Capability

from homeassistant.const import (
    CONCENTRATION_PARTS_PER_MILLION,
    DEVICE_CLASS_BATTERY,
    DEVICE_CLASS_HUMIDITY,
    DEVICE_CLASS_ILLUMINANCE,
    DEVICE_CLASS_TEMPERATURE,
    DEVICE_CLASS_TIMESTAMP,
    ENERGY_KILO_WATT_HOUR,
    MASS_KILOGRAMS,
    POWER_WATT,
    TEMP_CELSIUS,
    TEMP_FAHRENHEIT,
    UNIT_PERCENTAGE,
    VOLT,
)

from . import SmartThingsEntity
from .const import DATA_BROKERS, DOMAIN

Map = namedtuple("map", "attribute name default_unit device_class")

CAPABILITY_TO_SENSORS = {
    Capability.activity_lighting_mode: [
        Map(Attribute.lighting_mode, "Activity Lighting Mode", None, None)
    ],
    Capability.air_conditioner_mode: [
        Map(Attribute.air_conditioner_mode, "Air Conditioner Mode", None, None)
    ],
    Capability.spi_mode: [
        Map(Attribute.spi_mode, "SPI Mode", None, None)
    ],
    Capability.ac_optional_mode: [
        Map(Attribute.ac_optional_mode, "Optional Mode", None, None)
    ],
    Capability.air_flow_direction: [
        Map(Attribute.air_flow_direction, "Air Flow Direction", None, None)
    ],
    Capability.autoclean_mode: [
        Map(Attribute.autoclean_mode, "Autoclean Mode", None, None)
    ],
    Capability.air_quality_sensor: [
        Map(Attribute.air_quality, "Air Quality", "CAQI", None)
    ],
    Capability.alarm: [Map(Attribute.alarm, "Alarm", None, None)],
    Capability.audio_volume: [Map(Attribute.volume, "Volume", UNIT_PERCENTAGE, None)],
    Capability.battery: [
        Map(Attribute.battery, "Battery", UNIT_PERCENTAGE, DEVICE_CLASS_BATTERY)
    ],
    Capability.body_mass_index_measurement: [
        Map(Attribute.bmi_measurement, "Body Mass Index", f"{MASS_KILOGRAMS}/m^2", None)
    ],
    Capability.body_weight_measurement: [
        Map(Attribute.body_weight_measurement, "Body Weight", MASS_KILOGRAMS, None)
    ],
    Capability.carbon_dioxide_measurement: [
        Map(
            Attribute.carbon_dioxide,
            "Carbon Dioxide Measurement",
            CONCENTRATION_PARTS_PER_MILLION,
            None,
        )
    ],
    Capability.carbon_monoxide_detector: [
        Map(Attribute.carbon_monoxide, "Carbon Monoxide Detector", None, None)
    ],
    Capability.carbon_monoxide_measurement: [
        Map(
            Attribute.carbon_monoxide_level,
            "Carbon Monoxide Measurement",
            CONCENTRATION_PARTS_PER_MILLION,
            None,
        )
    ],
    Capability.dishwasher_operating_state: [
        Map(Attribute.machine_state, "Dishwasher Machine State", None, None),
        Map(Attribute.dishwasher_job_state, "Dishwasher Job State", None, None),
        Map(
            Attribute.completion_time,
            "Dishwasher Completion Time",
            None,
            DEVICE_CLASS_TIMESTAMP,
        ),
    ],
    Capability.dryer_mode: [Map(Attribute.dryer_mode, "Dryer Mode", None, None)],
    Capability.dryer_operating_state: [
        Map(Attribute.machine_state, "Dryer Machine State", None, None),
        Map(Attribute.dryer_job_state, "Dryer Job State", None, None),
        Map(
            Attribute.completion_time,
            "Dryer Completion Time",
            None,
            DEVICE_CLASS_TIMESTAMP,
        ),
    ],
    Capability.dust_sensor: [
        Map(Attribute.fine_dust_level, "Fine Dust Level", None, None),
        Map(Attribute.dust_level, "Dust Level", None, None),
    ],
    Capability.energy_meter: [
        Map(Attribute.energy, "Energy Meter", ENERGY_KILO_WATT_HOUR, None)
    ],
    Capability.equivalent_carbon_dioxide_measurement: [
        Map(
            Attribute.equivalent_carbon_dioxide_measurement,
            "Equivalent Carbon Dioxide Measurement",
            CONCENTRATION_PARTS_PER_MILLION,
            None,
        )
    ],
    Capability.formaldehyde_measurement: [
        Map(
            Attribute.formaldehyde_level,
            "Formaldehyde Measurement",
            CONCENTRATION_PARTS_PER_MILLION,
            None,
        )
    ],
    Capability.illuminance_measurement: [
        Map(Attribute.illuminance, "Illuminance", "lux", DEVICE_CLASS_ILLUMINANCE)
    ],
    Capability.infrared_level: [
        Map(Attribute.infrared_level, "Infrared Level", UNIT_PERCENTAGE, None)
    ],
    Capability.media_input_source: [
        Map(Attribute.input_source, "Media Input Source", None, None)
    ],
    Capability.media_playback_repeat: [
        Map(Attribute.playback_repeat_mode, "Media Playback Repeat", None, None)
    ],
    Capability.media_playback_shuffle: [
        Map(Attribute.playback_shuffle, "Media Playback Shuffle", None, None)
    ],
    Capability.media_playback: [
        Map(Attribute.playback_status, "Media Playback Status", None, None)
    ],
    Capability.odor_sensor: [Map(Attribute.odor_level, "Odor Sensor", None, None)],
    Capability.oven_mode: [Map(Attribute.oven_mode, "Oven Mode", None, None)],
    Capability.oven_operating_state: [
        Map(Attribute.machine_state, "Oven Machine State", None, None),
        Map(Attribute.oven_job_state, "Oven Job State", None, None),
        Map(Attribute.completion_time, "Oven Completion Time", None, None),
    ],
    Capability.oven_setpoint: [
        Map(Attribute.oven_setpoint, "Oven Set Point", None, None)
    ],
    Capability.power_meter: [Map(Attribute.power, "Power Meter", POWER_WATT, None)],
    Capability.power_source: [Map(Attribute.power_source, "Power Source", None, None)],
    Capability.refrigeration_setpoint: [
        Map(
            Attribute.refrigeration_setpoint,
            "Refrigeration Setpoint",
            None,
            DEVICE_CLASS_TEMPERATURE,
        )
    ],
    Capability.relative_humidity_measurement: [
        Map(
            Attribute.humidity,
            "Relative Humidity Measurement",
            UNIT_PERCENTAGE,
            DEVICE_CLASS_HUMIDITY,
        )
    ],
    Capability.robot_cleaner_cleaning_mode: [
        Map(
            Attribute.robot_cleaner_cleaning_mode,
            "Robot Cleaner Cleaning Mode",
            None,
            None,
        )
    ],
    Capability.robot_cleaner_movement: [
        Map(Attribute.robot_cleaner_movement, "Robot Cleaner Movement", None, None)
    ],
    Capability.robot_cleaner_turbo_mode: [
        Map(Attribute.robot_cleaner_turbo_mode, "Robot Cleaner Turbo Mode", None, None)
    ],
    Capability.signal_strength: [
        Map(Attribute.lqi, "LQI Signal Strength", None, None),
        Map(Attribute.rssi, "RSSI Signal Strength", None, None),
    ],
    Capability.smoke_detector: [Map(Attribute.smoke, "Smoke Detector", None, None)],
    Capability.temperature_measurement: [
        Map(
            Attribute.temperature,
            "Temperature Measurement",
            None,
            DEVICE_CLASS_TEMPERATURE,
        )
    ],
    Capability.thermostat_cooling_setpoint: [
        Map(
            Attribute.cooling_setpoint,
            "Thermostat Cooling Setpoint",
            None,
            DEVICE_CLASS_TEMPERATURE,
        )
    ],
    Capability.thermostat_fan_mode: [
        Map(Attribute.thermostat_fan_mode, "Thermostat Fan Mode", None, None)
    ],
    Capability.thermostat_heating_setpoint: [
        Map(
            Attribute.heating_setpoint,
            "Thermostat Heating Setpoint",
            None,
            DEVICE_CLASS_TEMPERATURE,
        )
    ],
    Capability.thermostat_mode: [
        Map(Attribute.thermostat_mode, "Thermostat Mode", None, None)
    ],
    Capability.thermostat_operating_state: [
        Map(
            Attribute.thermostat_operating_state,
            "Thermostat Operating State",
            None,
            None,
        )
    ],
    Capability.thermostat_setpoint: [
        Map(
            Attribute.thermostat_setpoint,
            "Thermostat Setpoint",
            None,
            DEVICE_CLASS_TEMPERATURE,
        )
    ],
    Capability.three_axis: [],
    Capability.tv_channel: [Map(Attribute.tv_channel, "Tv Channel", None, None)],
    Capability.tvoc_measurement: [
        Map(
            Attribute.tvoc_level,
            "Tvoc Measurement",
            CONCENTRATION_PARTS_PER_MILLION,
            None,
        )
    ],
    Capability.ultraviolet_index: [
        Map(Attribute.ultraviolet_index, "Ultraviolet Index", None, None)
    ],
    Capability.voltage_measurement: [
        Map(Attribute.voltage, "Voltage Measurement", VOLT, None)
    ],
    Capability.washer_mode: [Map(Attribute.washer_mode, "Washer Mode", None, None)],
    Capability.washer_operating_state: [
        Map(Attribute.machine_state, "Washer Machine State", None, None),
        Map(Attribute.washer_job_state, "Washer Job State", None, None),
        Map(
            Attribute.completion_time,
            "Washer Completion Time",
            None,
            DEVICE_CLASS_TIMESTAMP,
        ),
    ],
}

UNITS = {"C": TEMP_CELSIUS, "F": TEMP_FAHRENHEIT}

THREE_AXIS_NAMES = ["X Coordinate", "Y Coordinate", "Z Coordinate"]


async def async_setup_entry(hass, config_entry, async_add_entities):
    """Add binary sensors for a config entry."""
    broker = hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id]
    sensors = []
    for device in broker.devices.values():
        for capability in broker.get_assigned(device.device_id, "sensor"):
            if capability == Capability.three_axis:
                sensors.extend(
                    [
                        SmartThingsThreeAxisSensor(device, index)
                        for index in range(len(THREE_AXIS_NAMES))
                    ]
                )
            else:
                maps = CAPABILITY_TO_SENSORS[capability]
                sensors.extend(
                    [
                        SmartThingsSensor(
                            device, m.attribute, m.name, m.default_unit, m.device_class
                        )
                        for m in maps
                    ]
                )
    async_add_entities(sensors)


def get_capabilities(capabilities: Sequence[str]) -> Optional[Sequence[str]]:
    """Return all capabilities supported if minimum required are present."""
    return [
        capability for capability in CAPABILITY_TO_SENSORS if capability in capabilities
    ]


class SmartThingsSensor(SmartThingsEntity):
    """Define a SmartThings Sensor."""

    def __init__(
        self, device, attribute: str, name: str, default_unit: str, device_class: str
    ):
        """Init the class."""
        super().__init__(device)
        self._attribute = attribute
        self._name = name
        self._device_class = device_class
        self._default_unit = default_unit

    @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 state(self):
        """Return the state of the sensor."""
        return self._device.status.attributes[self._attribute].value

    @property
    def device_class(self):
        """Return the device class of the sensor."""
        return self._device_class

    @property
    def unit_of_measurement(self):
        """Return the unit this state is expressed in."""
        unit = self._device.status.attributes[self._attribute].unit
        return UNITS.get(unit, unit) if unit else self._default_unit


class SmartThingsThreeAxisSensor(SmartThingsEntity):
    """Define a SmartThings Three Axis Sensor."""

    def __init__(self, device, index):
        """Init the class."""
        super().__init__(device)
        self._index = index

    @property
    def name(self) -> str:
        """Return the name of the binary sensor."""
        return "{} {}".format(self._device.label, THREE_AXIS_NAMES[self._index])

    @property
    def unique_id(self) -> str:
        """Return a unique ID."""
        return "{}.{}".format(self._device.device_id, THREE_AXIS_NAMES[self._index])

    @property
    def state(self):
        """Return the state of the sensor."""
        three_axis = self._device.status.attributes[Attribute.three_axis].value
        try:
            return three_axis[self._index]
        except (TypeError, IndexError):
            return None

Continue on next post…

2 Likes

And my climate.py

"""Support for climate devices through the SmartThings cloud API."""
import asyncio
from collections.abc import Iterable
import logging
from typing import Optional, Sequence

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,
    CURRENT_HVAC_COOL,
    CURRENT_HVAC_FAN,
    CURRENT_HVAC_HEAT,
    CURRENT_HVAC_IDLE,
    HVAC_MODE_AUTO,
    HVAC_MODE_COOL,
    HVAC_MODE_DRY,
    HVAC_MODE_FAN_ONLY,
    HVAC_MODE_HEAT,
    HVAC_MODE_HEAT_COOL,
    HVAC_MODE_OFF,
    SUPPORT_FAN_MODE,
    SUPPORT_TARGET_TEMPERATURE,
    SUPPORT_TARGET_TEMPERATURE_RANGE,
)
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT

from . import SmartThingsEntity
from .const import DATA_BROKERS, DOMAIN

ATTR_OPERATION_STATE = "operation_state"
MODE_TO_STATE = {
    "auto": HVAC_MODE_HEAT_COOL,
    "cool": HVAC_MODE_COOL,
    "eco": HVAC_MODE_AUTO,
    "rush hour": HVAC_MODE_AUTO,
    "emergency heat": HVAC_MODE_HEAT,
    "heat": HVAC_MODE_HEAT,
    "wind": HVAC_MODE_FAN_ONLY,
	"off": HVAC_MODE_OFF,
}
STATE_TO_MODE = {
    HVAC_MODE_HEAT_COOL: "auto",
    HVAC_MODE_COOL: "cool",
    HVAC_MODE_HEAT: "heat",
    HVAC_MODE_OFF: "off",
	HVAC_MODE_FAN_ONLY: "wind",
}

OPERATING_STATE_TO_ACTION = {
    "cooling": CURRENT_HVAC_COOL,
    "fan only": CURRENT_HVAC_FAN,
    "heating": CURRENT_HVAC_HEAT,
    "idle": CURRENT_HVAC_IDLE,
    "pending cool": CURRENT_HVAC_COOL,
    "pending heat": CURRENT_HVAC_HEAT,
    "vent economizer": CURRENT_HVAC_FAN,
}

AC_MODE_TO_STATE = {
    "auto": HVAC_MODE_HEAT_COOL,
    "cool": HVAC_MODE_COOL,
    "dry": HVAC_MODE_DRY,
    "coolClean": HVAC_MODE_COOL,
    "dryClean": HVAC_MODE_DRY,
    "heat": HVAC_MODE_HEAT,
    "heatClean": HVAC_MODE_HEAT,
    "wind": HVAC_MODE_FAN_ONLY,
}
STATE_TO_AC_MODE = {
    HVAC_MODE_HEAT_COOL: "auto",
    HVAC_MODE_COOL: "cool",
    HVAC_MODE_DRY: "dry",
    HVAC_MODE_HEAT: "heat",
    HVAC_MODE_FAN_ONLY: "wind",
}

UNIT_MAP = {"C": TEMP_CELSIUS, "F": TEMP_FAHRENHEIT}

_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]) -> Optional[Sequence[str]]:
    """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,
    ]
    # 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 = SUPPORT_TARGET_TEMPERATURE | SUPPORT_TARGET_TEMPERATURE_RANGE
        if self._device.get_capability(
            Capability.thermostat_fan_mode, Capability.thermostat
        ):
            flags |= SUPPORT_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
        operation_state = kwargs.get(ATTR_HVAC_MODE)
        if operation_state:
            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 == HVAC_MODE_HEAT:
            heating_setpoint = kwargs.get(ATTR_TEMPERATURE)
        elif self.hvac_mode == HVAC_MODE_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:
                state = MODE_TO_STATE.get(mode)
                if state 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 ocf_data(self):
        """Return the OCF data property."""
        return self._device.status.data

    @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) -> Optional[str]:
        """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 == HVAC_MODE_COOL:
            return self._device.status.cooling_setpoint
        if self.hvac_mode == HVAC_MODE_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 == HVAC_MODE_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 == HVAC_MODE_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."""

    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_hvac_mode(self, hvac_mode):
        """Set new target operation mode."""
        if hvac_mode == HVAC_MODE_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
        operation_mode = kwargs.get(ATTR_HVAC_MODE)
        if operation_mode:
            if operation_mode == HVAC_MODE_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 = {HVAC_MODE_OFF}
        for mode in self._device.status.supported_ac_modes:
            state = AC_MODE_TO_STATE.get(mode)
            if state 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_temperature(self):
        """Return the current temperature."""
        return self._device.status.temperature

    @property
    def device_state_attributes(self):
        """
        Return device specific state attributes.

        Include attributes from the Demand Response Load Control (drlc)
        and Power Consumption capabilities.
        """
        attributes = [
            "drlc_status_duration",
            "drlc_status_level",
            "drlc_status_start",
            "drlc_status_override",
            "power_consumption_start",
            "power_consumption_power",
            "power_consumption_energy",
            "power_consumption_end",
            "data",
        ]
        state_attributes = {}
        for attribute in attributes:
            value = getattr(self._device.status, attribute)
            if value is not None:
                state_attributes[attribute] = value
        return state_attributes

    @property
    def ocf_data(self):
        """Return the OCF data property."""
        return self._device.status.data

    @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 hvac_mode(self):
        """Return current operation ie. heat, cool, idle."""
        if not self._device.status.switch:
            return HVAC_MODE_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."""
        return SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        return self._device.status.cooling_setpoint

    @property
    def temperature_unit(self):
        """Return the unit of measurement."""
        return UNIT_MAP.get(self._device.status.attributes[Attribute.temperature].unit)

Please note that climate have a bug with Wind Mode, to bypass this you have to register your climate using cool (or other mode, not wind), after you put this information on your customize.yaml (to fix modes and min/max temp)

climate.your_climate_device:
  hvac_modes:
  - cool
  - heat_cool
  - heat
  - 'off'
  - dry
  - fan_only
  max_temp: 30
  min_temp: 16
  supported_features: 9

Finally you can get sensors from custom capabilities and also can send commands to the unit using rest_command (as mentioned before)

To all OCF capabilities (that came on ‘execute/data’ attribute) I’ve created input values and set all of them using automation, this is for the light (I’ve this for light, sound/beep, custom modes, etc)

- id: acsuiteluzon
  alias: AcSuiteLuzOn
  description: ''
  trigger:
  - entity_id: climate.your_climate_device
    platform: state
  condition:
  - condition: template
    value_template: "{{ trigger.from_state is none or\n     trigger.to_state is not\
      \ none and\n     trigger.from_state.attributes.data !=\n     trigger.to_state.attributes.data\
      \ }}\n"
  - condition: template
    value_template: '{{ ''Light_Off'' in trigger.to_state.attributes[''data''][''payload''][''x.com.samsung.da.options'']
      }}'
  action:
  - data: {}
    entity_id: input_boolean.acsuitelight
    service: input_boolean.turn_on
- id: acsuiteluzoff
  alias: AcSuiteLuzOff
  description: ''
  trigger:
  - entity_id: climate.your_climate_device
    platform: state
  condition:
  - condition: template
    value_template: "{{ trigger.from_state is none or\n     trigger.to_state is not\
      \ none and\n     trigger.from_state.attributes.data !=\n     trigger.to_state.attributes.data\
      \ }}\n"
  - condition: template
    value_template: '{{ ''Light_On'' in trigger.to_state.attributes[''data''][''payload''][''x.com.samsung.da.options'']
      }}'
  action:
  - data: {}
    entity_id: input_boolean.acsuitelight
    service: input_boolean.turn_off

Finally used this on my switchs file (remember to see my post about all rest_comand)

- platform: template
  switches:
    luzacsuite:
      value_template: "{{ is_state('input_boolean.acsuitelight', 'on') }}"
      turn_on:
        service: rest_command.ac_light_on
        data:
          device_id: "xxxxxxx"
      turn_off:
        service: rest_command.ac_light_off
        data:
          device_id: "xxxxxx"

I will try to make a better way to handle this OCF sensors/switchs to avoid using automation but for now it’s working and I’m also able to set device light off when I turn off all lights on ambient (and sun is below horizon)

So if someone know a better way to handle that I appreciate

I hope it can help and feel free to reply with your troubles using this.

2 Likes

Hi guys i try to get token from my ac but when a start actest.py its show me this result
traceback (most recent call last):
file “C:\1\actest.py”, line 1, in
import requests
modulenotfoundError: No module named ‘requests’
pls help me

i got wifikit mim-h03

Hello,
i have this problem when i request the token from my samsung ac

Traceback (most recent call last):
  File "actest.py", line 4, in <module>
    resp = s.post("https://192.168.0.70:2878/devicetoken/request", data={"DeviceToken":"xxxxxxxxxxx"}, headers=headers, stream=True, verify=False, cert='cert.pem')
  File "C:\Users\Ezio\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\sessions.py", line 578, in post
    return self.request('POST', url, data=data, json=json, **kwargs)
  File "C:\Users\Ezio\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\sessions.py", line 530, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Users\Ezio\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\sessions.py", line 643, in send
    r = adapter.send(request, **kwargs)
  File "C:\Users\Ezio\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\adapters.py", line 514, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='192.168.0.70', port=2878): Max retries exceeded with url: /devicetoken/request (Caused by SSLError(SSLError(1, '[SSL: DH_KEY_TOO_SMALL] dh key too small (_ssl.c:1108)')))

where am i wrong?

Can you share your work? It can be very useful.

someone add the @JRFabbi work to the official Smarthing Integrations…

Unfortunately it’s impossible because its use a modified version of pysmartthings.

If you want I can help you to modify your installation to have this working

Do a PR in the pysmartthings github…

Hello @SebuZet , Needing your help is possible.

I’ve spent the whole day trying to retrieve the token from my Samsung MIM-H03 WiFi Controller. Which apparently works with Climate_IP. I am getting stuck. Have tried all the above instructions through Windows PC but I am stuck running the actest.py with error below.

I’ve tried other methods to try retrieve token, including standing up a Ubuntu server but encounter CA MD5 errors as newer OpenSSL would not allow the connection.

Anyone can help?

Traceback (most recent call last):
  File "c:\SamsungAC\actest.py", line 4, in <module>
    resp = s.post("https://10.1.1.100:8888/devicetoken/request", data={"DeviceToken":"xxxxxxxxxxx"}, headers=headers, stream=True, verify=False, cert='ac14k_m.pem')
  File "C:\Users\lawre\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\requests\sessions.py", line 590, in post
    return self.request('POST', url, data=data, json=json, **kwargs)