AC: bind SmartCoolClean odor-controller option tokens; add Czech translation
- airconditioner.py: odor_controller_active binary_sensor + odor_controller_progress sensor, read from the /mode/vs/0 SmartCoolClean_/ProgressSmartClean_ option tokens (SmartThings cloud custom.airConditionerOdorController capability). Read-only -- no confirmed write command. - Reporter's TP1X_DA-AC-RAC-01001_0000 dump: fan speed and WindFree preset already bind via the existing composite climate entity, no code change needed there. - en.json/nl.json: new entity names for the two additions. - cs.json: new, full Czech translation catalog (mirrors en.json topology 1:1). - New fixture + golden + tests locking in the odor-controller behavior.
This commit is contained in:
@@ -380,6 +380,29 @@ def _option_number_write(prefix):
|
|||||||
return write
|
return write
|
||||||
|
|
||||||
|
|
||||||
|
def _odor_controller_active(rep):
|
||||||
|
"""Odor-controller self-clean on/off, from the `SmartCoolClean_<On/Off>`
|
||||||
|
/mode/vs/0 option token -- corroborated against the SmartThings cloud
|
||||||
|
custom.airConditionerOdorController capability's airConditionerOdorController
|
||||||
|
State field, same on/off vocabulary and a matching name ("Smart Cool
|
||||||
|
Clean" mirrors "odor controller"). Unconditional across board
|
||||||
|
generations, same as beep above -- no evidence ties this token to the
|
||||||
|
legacy-vs-newer split _has_option_token gates on. Read-only: no command
|
||||||
|
capability is confirmed, so this stays a sensor rather than a guessed
|
||||||
|
write (the 'don't guess' rule)."""
|
||||||
|
tok = _option_token(rep, 'SmartCoolClean')
|
||||||
|
if tok is None:
|
||||||
|
return None
|
||||||
|
return tok == 'On'
|
||||||
|
|
||||||
|
|
||||||
|
def _odor_controller_progress(rep):
|
||||||
|
"""0-100 progress of the odor-controller self-clean cycle, from the
|
||||||
|
`ProgressSmartClean_<N>` option token -- mirrors the cloud
|
||||||
|
airConditionerOdorControllerProgress field."""
|
||||||
|
return _int(_option_token(rep, 'ProgressSmartClean'))
|
||||||
|
|
||||||
|
|
||||||
def _humidity(rep):
|
def _humidity(rep):
|
||||||
"""Relative humidity, preferring the 5%-rounded field where it exists.
|
"""Relative humidity, preferring the 5%-rounded field where it exists.
|
||||||
|
|
||||||
@@ -582,6 +605,21 @@ CLIMATE = Capability(
|
|||||||
exists_fn=_has_option_token('FilterTime'),
|
exists_fn=_has_option_token('FilterTime'),
|
||||||
device_class='duration', unit='h',
|
device_class='duration', unit='h',
|
||||||
state_class='measurement', icon='mdi:air-filter'),
|
state_class='measurement', icon='mdi:air-filter'),
|
||||||
|
# Odor-controller ("Smart Cool Clean") state + progress -- see
|
||||||
|
# _odor_controller_active's docstring for the cloud-capability
|
||||||
|
# correspondence. Present on TP1X_DA-AC-RAC-01001 (issue reporter's
|
||||||
|
# dump); gating is token presence only, not board generation.
|
||||||
|
BinarySensorDesc(key='odor_controller_active',
|
||||||
|
rep_fn=_odor_controller_active,
|
||||||
|
exists_fn=lambda rep, resources: (
|
||||||
|
_option_token(rep, 'SmartCoolClean') is not None),
|
||||||
|
icon='mdi:air-filter', entity_category='diagnostic'),
|
||||||
|
SensorDesc(key='odor_controller_progress',
|
||||||
|
rep_fn=_odor_controller_progress,
|
||||||
|
exists_fn=lambda rep, resources: (
|
||||||
|
_option_token(rep, 'ProgressSmartClean') is not None),
|
||||||
|
unit='%', state_class='measurement',
|
||||||
|
icon='mdi:progress-check', entity_category='diagnostic'),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,9 @@
|
|||||||
"overload_protection_active": {
|
"overload_protection_active": {
|
||||||
"name": "Overload protection active"
|
"name": "Overload protection active"
|
||||||
},
|
},
|
||||||
|
"odor_controller_active": {
|
||||||
|
"name": "Odor controller active"
|
||||||
|
},
|
||||||
"cycle_active": {
|
"cycle_active": {
|
||||||
"name": "Running"
|
"name": "Running"
|
||||||
},
|
},
|
||||||
@@ -646,6 +649,9 @@
|
|||||||
"outdoor_temperature": {
|
"outdoor_temperature": {
|
||||||
"name": "Outdoor temperature"
|
"name": "Outdoor temperature"
|
||||||
},
|
},
|
||||||
|
"odor_controller_progress": {
|
||||||
|
"name": "Odor controller progress"
|
||||||
|
},
|
||||||
"panel_status": {
|
"panel_status": {
|
||||||
"name": "Panel status"
|
"name": "Panel status"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,6 +34,9 @@
|
|||||||
"overload_protection_active": {
|
"overload_protection_active": {
|
||||||
"name": "Overbelastingsbeveiliging actief"
|
"name": "Overbelastingsbeveiliging actief"
|
||||||
},
|
},
|
||||||
|
"odor_controller_active": {
|
||||||
|
"name": "Geurbeheersing actief"
|
||||||
|
},
|
||||||
"cycle_active": {
|
"cycle_active": {
|
||||||
"name": "Actief"
|
"name": "Actief"
|
||||||
},
|
},
|
||||||
@@ -646,6 +649,9 @@
|
|||||||
"outdoor_temperature": {
|
"outdoor_temperature": {
|
||||||
"name": "Buitentemperatuur"
|
"name": "Buitentemperatuur"
|
||||||
},
|
},
|
||||||
|
"odor_controller_progress": {
|
||||||
|
"name": "Voortgang geurbeheersing"
|
||||||
|
},
|
||||||
"panel_status": {
|
"panel_status": {
|
||||||
"name": "Paneelstatus"
|
"name": "Paneelstatus"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,575 @@
|
|||||||
|
{
|
||||||
|
"device0": [
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
"href": "/aisleep/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.displayNightMode": "Off",
|
||||||
|
"x.com.samsung.da.elapsedTime": "0",
|
||||||
|
"x.com.samsung.da.requestFeedback": "Off",
|
||||||
|
"x.com.samsung.da.resultFeedback": 0,
|
||||||
|
"x.com.samsung.da.statusFeedback": "Idle",
|
||||||
|
"x.com.samsung.da.sleepTime": "14002200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/alarms/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.items": [
|
||||||
|
{
|
||||||
|
"x.com.samsung.da.id": "0",
|
||||||
|
"x.com.samsung.da.alarmType": "Device",
|
||||||
|
"x.com.samsung.da.code": "ErrorCode_OFF",
|
||||||
|
"x.com.samsung.da.triggeredTime": "2026-07-31T12:54:20",
|
||||||
|
"x.com.samsung.da.state": "Deleted"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x.com.samsung.da.id": "1",
|
||||||
|
"x.com.samsung.da.alarmType": "Device",
|
||||||
|
"x.com.samsung.da.code": "FilterAlarm_OFF",
|
||||||
|
"x.com.samsung.da.triggeredTime": "2026-07-31T12:54:20",
|
||||||
|
"x.com.samsung.da.state": "Deleted"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rt": [
|
||||||
|
"x.com.samsung.da.alarms"
|
||||||
|
],
|
||||||
|
"if": [
|
||||||
|
"oic.if.baseline",
|
||||||
|
"oic.if.a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/availablecontrolsets/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.sets": "AE3F00A0012C01610248000F0000",
|
||||||
|
"x.com.samsung.da.id": "RAC",
|
||||||
|
"x.com.samsung.da.version": "1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/configuration/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.region": "0000000000",
|
||||||
|
"x.com.samsung.da.airconOptionList": [
|
||||||
|
"PRODUCT_GLOBAL",
|
||||||
|
"AI_GLOBAL_HEATPUMP_4.0",
|
||||||
|
"AI_3.0",
|
||||||
|
"Auto_To_AI",
|
||||||
|
"AI_Heat",
|
||||||
|
"ENERGY_2.0",
|
||||||
|
"HOMECARE_WIZARD_V2",
|
||||||
|
"DR",
|
||||||
|
"SingleCommand_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/da/softreset/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.softwarereset": "false"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/drlc/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.start": "1970-01-01T00:00:00Z",
|
||||||
|
"x.com.samsung.da.override": "Off",
|
||||||
|
"x.com.samsung.da.durationminutes": "0",
|
||||||
|
"x.com.samsung.da.drlcLevel": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/electriccurrent/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"operation": "Off",
|
||||||
|
"modes": "3",
|
||||||
|
"supportedModes": [
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
"5",
|
||||||
|
"6",
|
||||||
|
"7",
|
||||||
|
"8",
|
||||||
|
"9"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/energy/consumption/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.cumulativeDate": "1785502435",
|
||||||
|
"x.com.samsung.da.cumulativeUnit": "Wh",
|
||||||
|
"x.com.samsung.da.instantaneousPowerUnit": "W"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/file/information/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.timeoffset": "+02:00",
|
||||||
|
"x.com.samsung.supprtedtype": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/filter/airdustfilter/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.filterUsageResolution": "1",
|
||||||
|
"x.com.samsung.da.filterCapacity": "500",
|
||||||
|
"x.com.samsung.da.filterCapacityUnit": "Hour",
|
||||||
|
"x.com.samsung.da.filterResetType": [
|
||||||
|
"replaceable",
|
||||||
|
"washable"
|
||||||
|
],
|
||||||
|
"x.com.samsung.da.filterStatus": "normal",
|
||||||
|
"x.com.samsung.da.filterUsage": "44",
|
||||||
|
"x.com.samsung.da.filterDesiredUsage": "500",
|
||||||
|
"x.com.samsung.da.supportedFilterDesiredUsage": [
|
||||||
|
"180",
|
||||||
|
"300",
|
||||||
|
"500",
|
||||||
|
"700"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/humidity/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.humidity": "0",
|
||||||
|
"x.com.samsung.da.fivepercentHumidity": "53"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/information/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.otnDUID": "**REDACTED**",
|
||||||
|
"x.com.samsung.da.modelNum": "TP1X_DA-AC-RAC-01001_0000|10267841|60010514001911010E00492200902000",
|
||||||
|
"x.com.samsung.da.description": "TP1X_DA-AC-RAC-01001_0000",
|
||||||
|
"x.com.samsung.da.serialNum": "**REDACTED**",
|
||||||
|
"x.com.samsung.da.serialNumOption": "**REDACTED**",
|
||||||
|
"x.com.samsung.da.items": [
|
||||||
|
{
|
||||||
|
"x.com.samsung.da.id": "0",
|
||||||
|
"x.com.samsung.da.description": "Version",
|
||||||
|
"x.com.samsung.da.type": "Software",
|
||||||
|
"x.com.samsung.da.number": "02762A260401",
|
||||||
|
"x.com.samsung.da.newVersionAvailable": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x.com.samsung.da.id": "1",
|
||||||
|
"x.com.samsung.da.description": "Version",
|
||||||
|
"x.com.samsung.da.type": "Firmware",
|
||||||
|
"x.com.samsung.da.newVersionAvailable": "0",
|
||||||
|
"x.com.samsung.da.number": "02678A24092500,FFFFFFFFFFFFFF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x.com.samsung.da.id": "2",
|
||||||
|
"x.com.samsung.da.description": "Version",
|
||||||
|
"x.com.samsung.da.type": "Firmware",
|
||||||
|
"x.com.samsung.da.newVersionAvailable": "0",
|
||||||
|
"x.com.samsung.da.number": "FFFFFFFFFFFFFF,FFFFFFFFFFFFFF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x.com.samsung.da.id": "3",
|
||||||
|
"x.com.samsung.da.description": "Version",
|
||||||
|
"x.com.samsung.da.type": "Firmware",
|
||||||
|
"x.com.samsung.da.newVersionAvailable": "0",
|
||||||
|
"x.com.samsung.da.number": "FFFFFFFFFFFFFF,FFFFFFFFFFFFFF"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"x.com.samsung.da.diagDumpType": "file",
|
||||||
|
"x.com.samsung.da.diagEndPoint": "SSM",
|
||||||
|
"x.com.samsung.da.diagLogType": [
|
||||||
|
"errCode",
|
||||||
|
"dump"
|
||||||
|
],
|
||||||
|
"x.com.samsung.da.diagMnid": "0AJT",
|
||||||
|
"x.com.samsung.da.diagSetupid": "AR2",
|
||||||
|
"x.com.samsung.da.diagProtocolType": "BLE_OCF",
|
||||||
|
"x.com.samsung.da.diagMinVersion": "3.0",
|
||||||
|
"x.com.samsung.da.diagTsId": "DA01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/keepnormalstate/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.keepnormal": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/light/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"mode": "On",
|
||||||
|
"supportedModes": [
|
||||||
|
"On",
|
||||||
|
"Off"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/mds/absencemonitoring/vs/0",
|
||||||
|
"rep": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/mds/absencestate/vs/0",
|
||||||
|
"rep": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/mode/convenient/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.modes": "Off",
|
||||||
|
"x.com.samsung.da.supportedModes": [
|
||||||
|
"Off",
|
||||||
|
"Sleep",
|
||||||
|
"Quiet",
|
||||||
|
"Speed",
|
||||||
|
"Nano",
|
||||||
|
"NanoSleep"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/mode/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.modes": [
|
||||||
|
"Cool"
|
||||||
|
],
|
||||||
|
"workingMode": "Cool",
|
||||||
|
"x.com.samsung.da.supportedModes": [
|
||||||
|
"Auto",
|
||||||
|
"Cool",
|
||||||
|
"Dry",
|
||||||
|
"Fan",
|
||||||
|
"Heat"
|
||||||
|
],
|
||||||
|
"x.com.samsung.da.options": [
|
||||||
|
"OptionCode_35880",
|
||||||
|
"Sleep_0",
|
||||||
|
"ArtificialWorking_Off",
|
||||||
|
"ComfortAICooling_Off",
|
||||||
|
"AiTempChanged_Off",
|
||||||
|
"AiTemp_240",
|
||||||
|
"Volume_Mute",
|
||||||
|
"StopAutoClean_Idle",
|
||||||
|
"SmartCoolClean_Off",
|
||||||
|
"ProgressSmartClean_0",
|
||||||
|
"FreezeAlarmSetting_Off",
|
||||||
|
"DesiredFreezeAlarm_240",
|
||||||
|
"WashAlarm_Off",
|
||||||
|
"ExtendOptionCode_215693",
|
||||||
|
"UpdateAllow_NotAllowed",
|
||||||
|
"DurationOn_240",
|
||||||
|
"OutdoorTemp_99",
|
||||||
|
"CoolCapa_20",
|
||||||
|
"WarmCapa_22",
|
||||||
|
"Autoclean_On",
|
||||||
|
"WelcomeCoolingState_Off",
|
||||||
|
"WelcomeHeatingState_Off",
|
||||||
|
"OutdoorConnection_Connected"
|
||||||
|
],
|
||||||
|
"rt": [
|
||||||
|
"x.com.samsung.da.mode"
|
||||||
|
],
|
||||||
|
"if": [
|
||||||
|
"oic.if.baseline",
|
||||||
|
"oic.if.a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/option/airpurify/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.modes": "Off",
|
||||||
|
"x.com.samsung.da.supportedModes": [
|
||||||
|
"On",
|
||||||
|
"Off"
|
||||||
|
],
|
||||||
|
"rt": [
|
||||||
|
"x.com.samsung.da.option.airpurify"
|
||||||
|
],
|
||||||
|
"if": [
|
||||||
|
"oic.if.baseline",
|
||||||
|
"oic.if.a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/option/autoclean/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.status": "Stop",
|
||||||
|
"x.com.samsung.da.supportedStatus": [
|
||||||
|
"Start",
|
||||||
|
"Stop"
|
||||||
|
],
|
||||||
|
"x.com.samsung.da.progress": "0",
|
||||||
|
"x.com.samsung.da.settingStatus": "On",
|
||||||
|
"x.com.samsung.da.supportedSettingStatus": [
|
||||||
|
"On",
|
||||||
|
"Off"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/option/muteonce/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"muteonce": "Off",
|
||||||
|
"rt": [
|
||||||
|
"x.com.samsung.da.option.muteonce"
|
||||||
|
],
|
||||||
|
"if": [
|
||||||
|
"oic.if.baseline",
|
||||||
|
"oic.if.a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/personality/presence/vs/0",
|
||||||
|
"rep": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/power/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.power": "On",
|
||||||
|
"operationNumber": "4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/realtimenotiforclient/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.timeforshortnoti": "0",
|
||||||
|
"x.com.samsung.da.longnotisubscription": "false",
|
||||||
|
"x.com.samsung.da.periodicnotisubscription": "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/remotedatacontrol/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.status": "Off",
|
||||||
|
"x.com.samsung.da.connectionStatus": "Disconnected"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/remotetemperature/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.temperature": "",
|
||||||
|
"x.com.samsung.da.unit": "",
|
||||||
|
"x.com.samsung.da.error": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/reserverulesets/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.sets": "AEFFFFFFFF101E121EFFFF101EFFFF000001001F00010000001F0000009C00FFFF1E00",
|
||||||
|
"x.com.samsung.da.id": "RAC",
|
||||||
|
"x.com.samsung.da.version": "1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/selfcheck/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.supportedActions": [
|
||||||
|
"Start",
|
||||||
|
"Cancel"
|
||||||
|
],
|
||||||
|
"x.com.samsung.da.error": [
|
||||||
|
"DA_ERROR_NONE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/temperature/control/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.increment": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/temperatures/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.items": [
|
||||||
|
{
|
||||||
|
"x.com.samsung.da.id": "0",
|
||||||
|
"x.com.samsung.da.description": "Temperature",
|
||||||
|
"x.com.samsung.da.current": "24.0",
|
||||||
|
"x.com.samsung.da.desired": "24.0",
|
||||||
|
"x.com.samsung.da.minimum": "16",
|
||||||
|
"x.com.samsung.da.maximum": "30",
|
||||||
|
"x.com.samsung.da.increment": "1",
|
||||||
|
"x.com.samsung.da.unit": "Celsius"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/welcome/temperature/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"operatingStatus": "None",
|
||||||
|
"requestId": "0000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/wind/direction/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.modes": "All",
|
||||||
|
"x.com.samsung.da.supportedModes": [
|
||||||
|
"Fix",
|
||||||
|
"Up_And_Low",
|
||||||
|
"Left_And_Right",
|
||||||
|
"All"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/wind/strength/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.modes": "3",
|
||||||
|
"x.com.samsung.da.supportedModes": [
|
||||||
|
"0",
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4"
|
||||||
|
],
|
||||||
|
"x.com.samsung.da.modesName": [
|
||||||
|
"Auto",
|
||||||
|
"Low",
|
||||||
|
"Mid",
|
||||||
|
"High",
|
||||||
|
"Turbo"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/otninformation/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"x.com.samsung.da.target": "",
|
||||||
|
"x.com.samsung.da.newVersionAvailable": "false",
|
||||||
|
"otnStatus": "None",
|
||||||
|
"flashingProgress": "0",
|
||||||
|
"otnCompleteDate": "noHistory",
|
||||||
|
"scheduledTime": "None",
|
||||||
|
"swVersionInfo": {
|
||||||
|
"platform": "Tizen Lite",
|
||||||
|
"oneUiVersion": "7.0 Air conditioner",
|
||||||
|
"osVersion": "4.0"
|
||||||
|
},
|
||||||
|
"otnList": [
|
||||||
|
{
|
||||||
|
"type": "WIFI",
|
||||||
|
"modelId": "ARA-KR-TP1-25-ARXX00",
|
||||||
|
"versions": [
|
||||||
|
"11260401"
|
||||||
|
],
|
||||||
|
"visVersion": "260401"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Micom",
|
||||||
|
"modelId": "845210267841FFFFFFFF",
|
||||||
|
"versions": [
|
||||||
|
"24092500",
|
||||||
|
"FFFFFFFF"
|
||||||
|
],
|
||||||
|
"visVersion": "240925"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Micom",
|
||||||
|
"modelId": "045210267841FFFFFFFF",
|
||||||
|
"versions": [
|
||||||
|
"24092500",
|
||||||
|
"FFFFFFFF"
|
||||||
|
],
|
||||||
|
"visVersion": "240925"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Micom",
|
||||||
|
"modelId": "FGCAN2",
|
||||||
|
"versions": [
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"visVersion": "999999"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Micom",
|
||||||
|
"modelId": "FGCAN3",
|
||||||
|
"versions": [
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"visVersion": "999999"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/timezone/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"timezoneid": "Europe/Prague",
|
||||||
|
"offset": "+02:00",
|
||||||
|
"DST": "ON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/connectionconfig/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"autoReconnectionMinVersion": "1.0",
|
||||||
|
"autoReconnection": "true",
|
||||||
|
"autoReconnectionProtocolType": [
|
||||||
|
"helper_hotspot",
|
||||||
|
"ble_ocf"
|
||||||
|
],
|
||||||
|
"supportedWiFiAuthType": [
|
||||||
|
"OPEN",
|
||||||
|
"WEP",
|
||||||
|
"WPA-PSK",
|
||||||
|
"WPA2-PSK",
|
||||||
|
"SAE"
|
||||||
|
],
|
||||||
|
"supportedWiFiCryptoType": [
|
||||||
|
"TKIP",
|
||||||
|
"AES",
|
||||||
|
"WEP-64",
|
||||||
|
"WEP-128"
|
||||||
|
],
|
||||||
|
"supportedWiFiFreq": [
|
||||||
|
"2.4G"
|
||||||
|
],
|
||||||
|
"calmConnectionCare": {
|
||||||
|
"version": "1.0",
|
||||||
|
"role": [
|
||||||
|
"things"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/wirelessinfo/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"macaddressWiFi": "**REDACTED**",
|
||||||
|
"macaddressBLE": "**REDACTED**",
|
||||||
|
"connectedApSsid": "REDACTED_SSID"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/quickcontrol/info/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"supportedVersion": "1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "/dginformation/vs/0",
|
||||||
|
"rep": {
|
||||||
|
"enrolmentstatus": "Unknown",
|
||||||
|
"devicestate": "Unknown",
|
||||||
|
"lockstatus": "Unknown",
|
||||||
|
"nextduedate": "",
|
||||||
|
"workingminutes": 0,
|
||||||
|
"paymentinfo": {
|
||||||
|
"emiplan": "Unknown",
|
||||||
|
"currency": "Unknown",
|
||||||
|
"totalemi": 0,
|
||||||
|
"totalemipaid": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"state_keys": [
|
||||||
|
"air_filter_status",
|
||||||
|
"air_filter_threshold",
|
||||||
|
"air_filter_usage",
|
||||||
|
"air_filter_usage_hours",
|
||||||
|
"air_purify",
|
||||||
|
"alarm_code",
|
||||||
|
"auto_clean",
|
||||||
|
"beep",
|
||||||
|
"climate",
|
||||||
|
"current_limit_enabled",
|
||||||
|
"current_limit_level",
|
||||||
|
"current_temperature_c",
|
||||||
|
"display_light",
|
||||||
|
"firmware_update",
|
||||||
|
"humidity",
|
||||||
|
"mute_once",
|
||||||
|
"odor_controller_active",
|
||||||
|
"odor_controller_progress",
|
||||||
|
"selfcheck_error",
|
||||||
|
"selfcheck_result",
|
||||||
|
"selfcheck_status",
|
||||||
|
"tropical_night_mode"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -872,3 +872,83 @@ def test_non_legacy_board_energy_kwh_still_uses_plain_wh_scale():
|
|||||||
state = flatten(
|
state = flatten(
|
||||||
discover(resources, reg.capabilities, reg.pattern_capabilities), resources)
|
discover(resources, reg.capabilities, reg.pattern_capabilities), resources)
|
||||||
assert state['energy_kwh'] == round(1686632 / 1000.0, 2)
|
assert state['energy_kwh'] == round(1686632 / 1000.0, 2)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TP1X_DA-AC-RAC-01001_0000, reporter's dump. Reported "fan and WindFree are
|
||||||
|
# missing" -- both already work: /wind/strength/vs/0's 0-4 codes match
|
||||||
|
# _DEVICE_TO_FAN exactly, and /mode/convenient/vs/0's Nano/NanoSleep codes
|
||||||
|
# already resolve dynamically via _preset_to_ha + the existing "nano"/
|
||||||
|
# "nanosleep" -> "WindFree"/"WindFree sleep" translation labels -- confirmed
|
||||||
|
# below by zero unbound hrefs and the climate entity binding with its usual
|
||||||
|
# FAN_MODE/PRESET_MODE features. The real, previously-uncaptured gap was
|
||||||
|
# /mode/vs/0's SmartCoolClean_/ProgressSmartClean_ option tokens (the cloud
|
||||||
|
# custom.airConditionerOdorController capability) -- see
|
||||||
|
# _odor_controller_active's docstring.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _ac_odor_controller():
|
||||||
|
return _resolve('airconditioner_tp1x_rac_odor_controller')
|
||||||
|
|
||||||
|
|
||||||
|
def test_odor_controller_no_unbound_hrefs():
|
||||||
|
reg, resources = _ac_odor_controller()
|
||||||
|
unbound = []
|
||||||
|
discover(resources, reg.capabilities, reg.pattern_capabilities, log=unbound.append)
|
||||||
|
assert unbound == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_odor_controller_fan_and_windfree_already_bind():
|
||||||
|
"""The actually-reported gap wasn't real: fan speed and the WindFree
|
||||||
|
preset both come from the composite climate entity, which is bound here
|
||||||
|
with its normal fan/preset feature set -- no code change needed for
|
||||||
|
either."""
|
||||||
|
reg, resources = _ac_odor_controller()
|
||||||
|
bound = discover(resources, reg.capabilities, reg.pattern_capabilities)
|
||||||
|
climate = [b for b in bound if isinstance(b.desc, ClimateDesc)]
|
||||||
|
assert len(climate) == 1 and climate[0].href == '/mode/vs/0'
|
||||||
|
assert resources['/wind/strength/vs/0']['x.com.samsung.da.supportedModes'] == [
|
||||||
|
'0', '1', '2', '3', '4']
|
||||||
|
assert resources['/mode/convenient/vs/0']['x.com.samsung.da.supportedModes'] == [
|
||||||
|
'Off', 'Sleep', 'Quiet', 'Speed', 'Nano', 'NanoSleep']
|
||||||
|
|
||||||
|
|
||||||
|
def test_odor_controller_state_and_progress_present():
|
||||||
|
reg, resources = _ac_odor_controller()
|
||||||
|
state = flatten(
|
||||||
|
discover(resources, reg.capabilities, reg.pattern_capabilities), resources)
|
||||||
|
assert state['odor_controller_active'] is False # SmartCoolClean_Off
|
||||||
|
assert state['odor_controller_progress'] == 0 # ProgressSmartClean_0
|
||||||
|
|
||||||
|
|
||||||
|
def test_odor_controller_read_from_option_tokens():
|
||||||
|
assert airconditioner._odor_controller_active(
|
||||||
|
{'x.com.samsung.da.options': ['SmartCoolClean_On']}) is True
|
||||||
|
assert airconditioner._odor_controller_active(
|
||||||
|
{'x.com.samsung.da.options': ['SmartCoolClean_Off']}) is False
|
||||||
|
assert airconditioner._odor_controller_active(
|
||||||
|
{'x.com.samsung.da.options': ['Volume_100']}) is None
|
||||||
|
assert airconditioner._odor_controller_progress(
|
||||||
|
{'x.com.samsung.da.options': ['ProgressSmartClean_42']}) == 42
|
||||||
|
assert airconditioner._odor_controller_progress({}) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_odor_controller_absent_when_no_smartcoolclean_token():
|
||||||
|
"""The original issue #17 dump's /mode/vs/0 options carry no
|
||||||
|
SmartCoolClean_/ProgressSmartClean_ tokens -- neither entity binds."""
|
||||||
|
reg, resources = _ac()
|
||||||
|
state = flatten(
|
||||||
|
discover(resources, reg.capabilities, reg.pattern_capabilities), resources)
|
||||||
|
assert 'odor_controller_active' not in state
|
||||||
|
assert 'odor_controller_progress' not in state
|
||||||
|
|
||||||
|
|
||||||
|
def test_odor_controller_is_read_only():
|
||||||
|
"""No command capability is confirmed for SmartCoolClean -- exposed
|
||||||
|
read-only, same 'don't guess' precedent as CURRENT_LIMIT/ANOMALY_LOAD."""
|
||||||
|
active = next(e for e in airconditioner.CLIMATE.entities
|
||||||
|
if e.key == 'odor_controller_active')
|
||||||
|
progress = next(e for e in airconditioner.CLIMATE.entities
|
||||||
|
if e.key == 'odor_controller_progress')
|
||||||
|
assert not hasattr(active, 'write_fn') or active.write_fn is None
|
||||||
|
assert not hasattr(progress, 'write_fn') or progress.write_fn is None
|
||||||
|
|||||||
@@ -810,6 +810,27 @@ def test_registry_reproduces_golden_state_keys_for_airconditioner_tp1x_rac_01001
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_registry_reproduces_golden_state_keys_for_airconditioner_tp1x_rac_odor_controller():
|
||||||
|
"""TP1X_DA-AC-RAC-01001_0000 -- reporter's dump: fan (0-4 wind-strength
|
||||||
|
scale) and WindFree (Nano/NanoSleep convenient-mode codes) already bind
|
||||||
|
via the composite climate entity, no code change needed there. The
|
||||||
|
genuine gap was /mode/vs/0's SmartCoolClean_/ProgressSmartClean_ option
|
||||||
|
tokens (the cloud custom.airConditionerOdorController capability),
|
||||||
|
previously unbound to any entity -- now odor_controller_active/
|
||||||
|
odor_controller_progress."""
|
||||||
|
from tests.conftest import _load_device
|
||||||
|
resources = _load_device('airconditioner_tp1x_rac_odor_controller')
|
||||||
|
golden = json.loads(
|
||||||
|
(GOLDEN / 'airconditioner_tp1x_rac_odor_controller.json').read_text()
|
||||||
|
)
|
||||||
|
state_keys = _new_state_keys('airconditioner_tp1x_rac_odor_controller', resources)
|
||||||
|
assert set(state_keys) == set(golden['state_keys']), (
|
||||||
|
f"state_keys mismatch:\n"
|
||||||
|
f" extra: {sorted(set(state_keys) - set(golden['state_keys']))}\n"
|
||||||
|
f" missing: {sorted(set(golden['state_keys']) - set(state_keys))}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_registry_reproduces_golden_state_keys_for_air_dresser():
|
def test_registry_reproduces_golden_state_keys_for_air_dresser():
|
||||||
"""DA_DF_A51_20_COMMON AirDresser (model DF8600T, issue #162) -- reports
|
"""DA_DF_A51_20_COMMON AirDresser (model DF8600T, issue #162) -- reports
|
||||||
no oneUiVersion; resolved via the '_DF_' modelNum token fallback. Has no
|
no oneUiVersion; resolved via the '_DF_' modelNum token fallback. Has no
|
||||||
|
|||||||
Reference in New Issue
Block a user