homebridge-samsung-window-ac
Version:
Homebridge plugin for Samsung Window Air Conditioners
742 lines • 38.4 kB
JavaScript
// AC Mode mapping constants
const AC_MODE_MAPPING = {
// Samsung AC modes to HomeKit modes
'off': 0, // Off
'cool': 2, // Cool
'dry': 1, // Heat (mapped to Dry)
'aIComfort': 3, // Auto (mapped to AI Comfort)
'fan': 0, // Fan mode ignored, mapped to Off
};
const HOMEKIT_MODE_MAPPING = {
// HomeKit modes to Samsung AC modes
0: 'off', // Off
1: 'dry', // Heat -> Dry
2: 'cool', // Cool
3: 'aIComfort', // Auto -> AI Comfort
};
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
export class SamsungWindowACAccessory {
platform;
accessory;
service;
humidityService;
/**
* These are used to track the state of the air conditioner
*/
acStates = {
Active: false,
CurrentHeatingCoolingState: 0, // 0: Off, 1: Heat, 2: Cool
TargetHeatingCoolingState: 0, // 0: Off, 1: Heat, 2: Cool, 3: Auto
CurrentTemperature: 24,
TargetTemperature: 24,
CoolingThresholdTemperature: 24,
HeatingThresholdTemperature: 24,
CurrentHumidity: 50,
TemperatureDisplayUnits: 0, // 0: Celsius, 1: Fahrenheit
};
/**
* Cache for device status to reduce API calls
*/
statusCache = null;
CACHE_DURATION = 5 * 60 * 1000; // 5 minutes in milliseconds
/**
* Individual characteristic caches to avoid unnecessary API calls
*/
characteristicCaches = {};
CHARACTERISTIC_CACHE_DURATION = 30 * 1000; // 30 seconds for individual characteristics
/**
* Convert Samsung AC mode to HomeKit mode
*/
samsungModeToHomeKit(samsungMode) {
return AC_MODE_MAPPING[samsungMode] ?? 0;
}
/**
* Convert HomeKit mode to Samsung AC mode
*/
homeKitModeToSamsung(homeKitMode) {
return HOMEKIT_MODE_MAPPING[homeKitMode] ?? 'off';
}
/**
* Clear all characteristic caches when device state changes
*/
clearCharacteristicCaches() {
this.characteristicCaches = {};
this.platform.log.debug('Cleared all characteristic caches due to state change');
}
/**
* Update specific characteristic cache with new value
*/
updateCharacteristicCache(key, value) {
this.characteristicCaches[key] = {
value,
timestamp: Date.now(),
};
this.platform.log.debug(`Updated cache for ${key} -> ${value}`);
}
/**
* Check if device physical state has changed (on/off)
*/
async checkPhysicalStateChange() {
const status = await this.getDeviceStatus();
if (!status) {
return false;
}
const currentPhysicalState = status.components.main.switch.switch.value === 'on';
const cachedPhysicalState = this.characteristicCaches.Active?.value;
// If no cached state or state has changed, return true
if (cachedPhysicalState === undefined || cachedPhysicalState !== currentPhysicalState) {
return true;
}
return false;
}
/**
* Get device status with caching
*/
async getDeviceStatus() {
const now = Date.now();
// Check if cache is valid
if (this.statusCache && (now - this.statusCache.timestamp) < this.CACHE_DURATION) {
this.platform.log.debug('Using cached device status');
return this.statusCache.data;
}
// Get fresh data from API
this.platform.log.debug('Device status retrieved from SmartThings API');
const status = await this.platform.getDeviceStatus();
if (status) {
this.statusCache = {
data: status,
timestamp: now,
};
this.platform.log.debug('Updated device status cache');
}
return status;
}
constructor(platform, accessory) {
this.platform = platform;
this.accessory = accessory;
// set accessory information
this.accessory.getService(this.platform.Service.AccessoryInformation)
.setCharacteristic(this.platform.Characteristic.Manufacturer, 'Samsung')
.setCharacteristic(this.platform.Characteristic.Model, accessory.context.device.model)
.setCharacteristic(this.platform.Characteristic.SerialNumber, accessory.context.device.serialNumber);
// get the Thermostat service if it exists, otherwise create a new Thermostat service
this.service = this.accessory.getService(this.platform.Service.Thermostat) ||
this.accessory.addService(this.platform.Service.Thermostat);
// set the service name, this is what is displayed as the default name on the Home app
this.service.setCharacteristic(this.platform.Characteristic.Name, accessory.context.device.displayName);
// Set initial temperature values and ranges
this.service.setCharacteristic(this.platform.Characteristic.TargetTemperature, 24);
this.service.setCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature, 24);
this.service.setCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature, 24);
this.service.setCharacteristic(this.platform.Characteristic.CurrentTemperature, 24);
// Set temperature display units to Celsius
this.service.setCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits, 0);
// Set initial heating/cooling states
this.service.setCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState, 0);
this.service.setCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState, 0);
// Set initial active state
this.service.setCharacteristic(this.platform.Characteristic.Active, false);
// Create humidity service
this.humidityService = this.accessory.getService(this.platform.Service.HumiditySensor) ||
this.accessory.addService(this.platform.Service.HumiditySensor);
// Set humidity service name
this.humidityService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.context.device.displayName} Humidity`);
// Set initial humidity value
this.humidityService.setCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, 50);
// each service must implement at-minimum the "required characteristics" for the given service type
// see https://developers.homebridge.io/#/service/Thermostat
// register handlers for the Active Characteristic
this.service.getCharacteristic(this.platform.Characteristic.Active)
.onSet(this.setActive.bind(this))
.onGet(this.getActive.bind(this));
// register handlers for the CurrentHeatingCoolingState Characteristic
this.service.getCharacteristic(this.platform.Characteristic.CurrentHeatingCoolingState)
.onGet(this.getCurrentHeatingCoolingState.bind(this));
// register handlers for the TargetHeatingCoolingState Characteristic
this.service.getCharacteristic(this.platform.Characteristic.TargetHeatingCoolingState)
.onSet(this.setTargetHeatingCoolingState.bind(this))
.onGet(this.getTargetHeatingCoolingState.bind(this));
// register handlers for the CurrentTemperature Characteristic
this.service.getCharacteristic(this.platform.Characteristic.CurrentTemperature)
.onGet(this.getCurrentTemperature.bind(this));
// register handlers for the TargetTemperature Characteristic
const targetTempChar = this.service.getCharacteristic(this.platform.Characteristic.TargetTemperature);
targetTempChar
.setProps({
minValue: 18,
maxValue: 30,
minStep: 1,
})
.onSet(this.setTargetTemperature.bind(this))
.onGet(this.getTargetTemperature.bind(this));
// register handlers for the CoolingThresholdTemperature Characteristic
const coolingThresholdChar = this.service.getCharacteristic(this.platform.Characteristic.CoolingThresholdTemperature);
coolingThresholdChar
.setProps({
minValue: 18,
maxValue: 30,
minStep: 1,
})
.onSet(this.setCoolingThresholdTemperature.bind(this))
.onGet(this.getCoolingThresholdTemperature.bind(this));
// register handlers for the HeatingThresholdTemperature Characteristic
const heatingThresholdChar = this.service.getCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature);
heatingThresholdChar
.setProps({
minValue: 18,
maxValue: 30,
minStep: 1,
})
.onSet(this.setHeatingThresholdTemperature.bind(this))
.onGet(this.getHeatingThresholdTemperature.bind(this));
// register handlers for the TemperatureDisplayUnits Characteristic
this.service.getCharacteristic(this.platform.Characteristic.TemperatureDisplayUnits)
.onSet(this.setTemperatureDisplayUnits.bind(this))
.onGet(this.getTemperatureDisplayUnits.bind(this));
// register handlers for the CurrentRelativeHumidity Characteristic
this.humidityService.getCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity)
.onGet(this.getCurrentHumidity.bind(this));
}
/**
* Handle "SET" requests from HomeKit for Active
*/
async setActive(value) {
const isActive = value;
this.platform.log.debug(`Set Characteristic Active -> ${isActive}`);
if (isActive) {
// Turn on AC - set to current target mode
const samsungMode = this.homeKitModeToSamsung(this.acStates.TargetHeatingCoolingState);
const success = await this.platform.setACMode(samsungMode);
if (success) {
this.acStates.Active = true;
this.acStates.CurrentHeatingCoolingState = this.acStates.TargetHeatingCoolingState;
// Update caches with new values
this.updateCharacteristicCache('Active', true);
this.updateCharacteristicCache('CurrentHeatingCoolingState', this.acStates.CurrentHeatingCoolingState);
this.updateCharacteristicCache('TargetHeatingCoolingState', this.acStates.TargetHeatingCoolingState);
this.platform.log.info(`Successfully turned on AC with mode: ${samsungMode}`);
}
else {
this.platform.log.error(`Failed to turn on AC with mode: ${samsungMode}`);
// Don't update local state if API call failed
}
}
else {
// Turn off AC using switch capability
const success = await this.platform.turnOffAC();
if (success) {
this.acStates.Active = false;
this.acStates.CurrentHeatingCoolingState = 0;
this.acStates.TargetHeatingCoolingState = 0;
// Update caches with new values
this.updateCharacteristicCache('Active', false);
this.updateCharacteristicCache('CurrentHeatingCoolingState', 0);
this.updateCharacteristicCache('TargetHeatingCoolingState', 0);
this.platform.log.info('Successfully turned off AC');
}
else {
this.platform.log.error('Failed to turn off AC');
// Don't update local state if API call failed
}
}
}
/**
* Handle "GET" requests from HomeKit for Active
*/
async getActive() {
const cacheKey = 'Active';
const now = Date.now();
// Check if physical state has changed
const physicalStateChanged = await this.checkPhysicalStateChange();
// If physical state changed, clear all caches and force refresh
if (physicalStateChanged) {
this.clearCharacteristicCaches();
this.platform.log.debug('Physical state changed, cleared all caches');
}
// Check if we have a valid cache for this characteristic
if (this.characteristicCaches[cacheKey] &&
(now - this.characteristicCaches[cacheKey].timestamp) < this.CHARACTERISTIC_CACHE_DURATION) {
const cachedValue = this.characteristicCaches[cacheKey].value;
this.platform.log.debug(`Get Characteristic Active -> ${cachedValue} (cached)`);
return cachedValue;
}
const status = await this.getDeviceStatus();
if (status) {
const isActive = status.components.main.switch.switch.value === 'on';
this.acStates.Active = isActive;
// Cache the result
this.characteristicCaches[cacheKey] = {
value: isActive,
timestamp: now,
};
this.platform.log.debug(`Get Characteristic Active -> ${isActive}`);
return isActive;
}
// Fallback to cached state if API fails
this.platform.log.debug(`Get Characteristic Active -> ${this.acStates.Active} (fallback)`);
return this.acStates.Active;
}
/**
* Handle "GET" requests from HomeKit for CurrentHeatingCoolingState
*/
async getCurrentHeatingCoolingState() {
const cacheKey = 'CurrentHeatingCoolingState';
const now = Date.now();
// Check if physical state has changed
const physicalStateChanged = await this.checkPhysicalStateChange();
// If physical state changed, clear all caches and force refresh
if (physicalStateChanged) {
this.clearCharacteristicCaches();
this.platform.log.debug('Physical state changed, cleared all caches');
}
// Check if we have a valid cache for this characteristic
if (this.characteristicCaches[cacheKey] &&
(now - this.characteristicCaches[cacheKey].timestamp) < this.CHARACTERISTIC_CACHE_DURATION) {
const cachedValue = this.characteristicCaches[cacheKey].value;
this.platform.log.debug(`Get Characteristic CurrentHeatingCoolingState -> ${cachedValue} (cached)`);
return cachedValue;
}
const status = await this.getDeviceStatus();
if (status) {
const acMode = status.components.main.airConditionerMode.airConditionerMode.value;
const isActive = status.components.main.switch.switch.value === 'on';
let currentState;
if (!isActive) {
currentState = 0; // Off
}
else {
const homeKitMode = this.samsungModeToHomeKit(acMode);
// HomeKit CurrentHeatingCoolingState max value is 2, so convert Auto (3) to Cool (2)
// But we'll use Cool (2) for Auto mode since it's the most appropriate representation
if (homeKitMode === 3) {
currentState = 2; // Auto -> Cool for current state
this.platform.log.debug('Auto mode detected, showing as Cool (2) for CurrentHeatingCoolingState');
}
else {
currentState = homeKitMode;
}
}
this.acStates.CurrentHeatingCoolingState = currentState;
// Cache the result
this.characteristicCaches[cacheKey] = {
value: currentState,
timestamp: now,
};
this.platform.log.debug(`Get Characteristic CurrentHeatingCoolingState -> ${currentState} (from mode: ${acMode})`);
return currentState;
}
// Fallback to cached state if API fails
this.platform.log.debug(`Get Characteristic CurrentHeatingCoolingState -> ${this.acStates.CurrentHeatingCoolingState} (fallback)`);
return this.acStates.CurrentHeatingCoolingState;
}
/**
* Handle "SET" requests from HomeKit for TargetHeatingCoolingState
*/
async setTargetHeatingCoolingState(value) {
const homeKitMode = value;
const samsungMode = this.homeKitModeToSamsung(homeKitMode);
this.platform.log.debug(`Set Characteristic TargetHeatingCoolingState (${homeKitMode} -> ${samsungMode})`);
let success = false;
if (homeKitMode === 0) {
// Off mode - use switch off command
success = await this.platform.turnOffAC();
}
else if (homeKitMode === 3) {
// Auto mode (aIComfort) - use HeatingThresholdTemperature as target
const targetTemp = this.acStates.HeatingThresholdTemperature;
success = await this.platform.setACMode(samsungMode);
if (success) {
// Set target temperature to HeatingThresholdTemperature value
await this.platform.setTargetTemperature(targetTemp);
// Update CoolingThresholdTemperature for Auto mode
const coolingThreshold = Math.min(targetTemp + 4, 30);
this.acStates.CoolingThresholdTemperature = coolingThreshold;
this.platform.log.debug(`Auto mode: Set CoolingThresholdTemperature to ${coolingThreshold}°C (HeatingThresholdTemperature + 4°C)`);
}
}
else {
// Other modes - use airConditionerMode command
success = await this.platform.setACMode(samsungMode);
}
if (success) {
this.acStates.TargetHeatingCoolingState = homeKitMode;
if (this.acStates.Active) {
// CurrentHeatingCoolingState cannot be Auto (3), so convert Auto to Cool (2)
let currentState = homeKitMode;
if (homeKitMode === 3) {
currentState = 2; // Auto -> Cool for current state
this.platform.log.debug('Auto mode set, CurrentHeatingCoolingState will show as Cool (2)');
}
this.acStates.CurrentHeatingCoolingState = currentState;
}
// Update caches with new values
this.updateCharacteristicCache('TargetHeatingCoolingState', homeKitMode);
this.updateCharacteristicCache('CurrentHeatingCoolingState', this.acStates.CurrentHeatingCoolingState);
// For Auto mode, also update temperature caches
if (homeKitMode === 3) {
this.updateCharacteristicCache('HeatingThresholdTemperature', this.acStates.HeatingThresholdTemperature);
this.updateCharacteristicCache('CoolingThresholdTemperature', this.acStates.CoolingThresholdTemperature);
this.updateCharacteristicCache('TargetTemperature', this.acStates.TargetTemperature);
}
if (homeKitMode === 0) {
this.acStates.Active = false;
this.updateCharacteristicCache('Active', false);
this.platform.log.info('Successfully turned off AC');
}
else if (homeKitMode === 3) {
this.acStates.Active = true;
this.updateCharacteristicCache('Active', true);
this.platform.log.info(`Successfully changed AC mode to ${samsungMode} (HomeKit: ${homeKitMode}) with target temperature: ${this.acStates.HeatingThresholdTemperature}°C`);
}
else {
this.acStates.Active = true;
this.updateCharacteristicCache('Active', true);
this.platform.log.info(`Successfully changed AC mode to ${samsungMode} (HomeKit: ${homeKitMode})`);
}
}
else {
this.platform.log.error(`Failed to change AC mode to ${samsungMode} (HomeKit: ${homeKitMode})`);
// Don't update local state if API call failed
}
}
/**
* Handle "GET" requests from HomeKit for TargetHeatingCoolingState
*/
async getTargetHeatingCoolingState() {
const cacheKey = 'TargetHeatingCoolingState';
const now = Date.now();
// Check if physical state has changed
const physicalStateChanged = await this.checkPhysicalStateChange();
// If physical state changed, clear all caches and force refresh
if (physicalStateChanged) {
this.clearCharacteristicCaches();
this.platform.log.debug('Physical state changed, cleared all caches');
}
// Check if we have a valid cache for this characteristic
if (this.characteristicCaches[cacheKey] &&
(now - this.characteristicCaches[cacheKey].timestamp) < this.CHARACTERISTIC_CACHE_DURATION) {
const cachedValue = this.characteristicCaches[cacheKey].value;
this.platform.log.debug(`Get Characteristic TargetHeatingCoolingState -> ${cachedValue} (cached)`);
return cachedValue;
}
const status = await this.getDeviceStatus();
if (status) {
const acMode = status.components.main.airConditionerMode.airConditionerMode.value;
const isActive = status.components.main.switch.switch.value === 'on';
const homeKitMode = this.samsungModeToHomeKit(acMode);
// If device is physically off, return 0 (Off)
if (!isActive) {
this.acStates.TargetHeatingCoolingState = 0;
// Cache the result
this.characteristicCaches[cacheKey] = {
value: 0,
timestamp: now,
};
this.platform.log.debug('Device is physically off, TargetHeatingCoolingState set to 0');
return 0;
}
this.acStates.TargetHeatingCoolingState = homeKitMode;
// Cache the result
this.characteristicCaches[cacheKey] = {
value: homeKitMode,
timestamp: now,
};
this.platform.log.debug(`Get Characteristic TargetHeatingCoolingState -> ${homeKitMode} (from mode: ${acMode})`);
return homeKitMode;
}
// Fallback to cached state if API fails
this.platform.log.debug(`Get Characteristic TargetHeatingCoolingState -> ${this.acStates.TargetHeatingCoolingState} (fallback)`);
return this.acStates.TargetHeatingCoolingState;
}
/**
* Handle "GET" requests from HomeKit for CurrentTemperature
*/
async getCurrentTemperature() {
const cacheKey = 'CurrentTemperature';
const now = Date.now();
// Check if we have a valid cache for this characteristic
if (this.characteristicCaches[cacheKey] &&
(now - this.characteristicCaches[cacheKey].timestamp) < this.CHARACTERISTIC_CACHE_DURATION) {
const cachedValue = this.characteristicCaches[cacheKey].value;
this.platform.log.debug(`Get Characteristic CurrentTemperature -> ${cachedValue}°C (cached)`);
return cachedValue;
}
const status = await this.getDeviceStatus();
if (status) {
const temperature = status.components.main.temperatureMeasurement.temperature.value;
this.acStates.CurrentTemperature = temperature;
// Cache the result
this.characteristicCaches[cacheKey] = {
value: temperature,
timestamp: now,
};
this.platform.log.debug(`Get Characteristic CurrentTemperature -> ${temperature}°C`);
return temperature;
}
// Fallback to cached state if API fails
this.platform.log.debug(`Get Characteristic CurrentTemperature -> ${this.acStates.CurrentTemperature}°C (fallback)`);
return this.acStates.CurrentTemperature;
}
/**
* Handle "SET" requests from HomeKit for TargetTemperature
*/
async setTargetTemperature(value) {
const temperature = value;
this.platform.log.debug(`Set Characteristic TargetTemperature -> ${temperature}°C`);
// Send command to SmartThings API to set target temperature
const success = await this.platform.setTargetTemperature(temperature);
if (success) {
this.acStates.TargetTemperature = temperature;
this.acStates.HeatingThresholdTemperature = temperature;
this.acStates.CoolingThresholdTemperature = temperature;
// Update caches with new values
this.updateCharacteristicCache('TargetTemperature', temperature);
this.updateCharacteristicCache('HeatingThresholdTemperature', temperature);
// For Auto mode, set CoolingThresholdTemperature to HeatingThresholdTemperature + 4°C (max 30°C)
if (this.acStates.TargetHeatingCoolingState === 3) { // Auto mode
const coolingThreshold = Math.min(temperature + 4, 30);
this.acStates.CoolingThresholdTemperature = coolingThreshold;
this.updateCharacteristicCache('CoolingThresholdTemperature', coolingThreshold);
this.platform.log.debug(`Auto mode: Updated CoolingThresholdTemperature to ${coolingThreshold}°C (HeatingThresholdTemperature + 4°C)`);
}
else {
this.updateCharacteristicCache('CoolingThresholdTemperature', temperature);
}
this.platform.log.info(`Successfully set target temperature to ${temperature}°C`);
}
else {
this.platform.log.error(`Failed to set target temperature to ${temperature}°C`);
// Don't update local state if API call failed
}
}
/**
* Handle "GET" requests from HomeKit for TargetTemperature
*/
async getTargetTemperature() {
const cacheKey = 'TargetTemperature';
const now = Date.now();
// Check if we have a valid cache for this characteristic
if (this.characteristicCaches[cacheKey] &&
(now - this.characteristicCaches[cacheKey].timestamp) < this.CHARACTERISTIC_CACHE_DURATION) {
const cachedValue = this.characteristicCaches[cacheKey].value;
this.platform.log.debug(`Get Characteristic TargetTemperature -> ${cachedValue}°C (cached)`);
return cachedValue;
}
const status = await this.getDeviceStatus();
if (status) {
const targetTemperature = status.components.main.thermostatCoolingSetpoint.coolingSetpoint.value;
const acMode = status.components.main.airConditionerMode.airConditionerMode.value;
const homeKitMode = this.samsungModeToHomeKit(acMode);
this.acStates.TargetTemperature = targetTemperature;
this.acStates.HeatingThresholdTemperature = targetTemperature;
// For Auto mode, set CoolingThresholdTemperature to HeatingThresholdTemperature + 4°C (max 30°C)
// This makes it easier to control in HomeKit when the knobs are not at the same point
if (homeKitMode === 3) { // Auto mode
const coolingThreshold = Math.min(targetTemperature + 4, 30);
this.acStates.CoolingThresholdTemperature = coolingThreshold;
this.platform.log.debug(`Auto mode: HeatingThresholdTemperature=${targetTemperature}°C, CoolingThresholdTemperature=${coolingThreshold}°C`);
}
else {
this.acStates.CoolingThresholdTemperature = targetTemperature;
}
// Cache the result
this.characteristicCaches[cacheKey] = {
value: targetTemperature,
timestamp: now,
};
this.platform.log.debug(`Get Characteristic TargetTemperature -> ${targetTemperature}°C`);
return targetTemperature;
}
// Fallback to cached state if API fails
this.platform.log.debug(`Get Characteristic TargetTemperature -> ${this.acStates.TargetTemperature}°C (fallback)`);
return this.acStates.TargetTemperature;
}
/**
* Handle "SET" requests from HomeKit for CoolingThresholdTemperature
*/
async setCoolingThresholdTemperature(value) {
const temperature = value;
// Validate temperature range
if (temperature < 18 || temperature > 30) {
this.platform.log.warn(`Cooling temperature ${temperature} is out of range (18-30). Clamping to valid range.`);
this.acStates.CoolingThresholdTemperature = Math.max(18, Math.min(30, temperature));
}
else {
this.acStates.CoolingThresholdTemperature = temperature;
}
this.platform.log.debug(`Set Characteristic CoolingThresholdTemperature -> ${this.acStates.CoolingThresholdTemperature}°C`);
// If in Auto mode, update HeatingThresholdTemperature to CoolingThresholdTemperature - 4°C (min 18°C)
if (this.acStates.TargetHeatingCoolingState === 3) {
const heatingThreshold = Math.max(this.acStates.CoolingThresholdTemperature - 4, 18);
this.acStates.HeatingThresholdTemperature = heatingThreshold;
this.platform.log.debug(`Auto mode: Updated HeatingThresholdTemperature to ${heatingThreshold}°C (CoolingThresholdTemperature - 4°C)`);
// Send command to SmartThings API to set target temperature to HeatingThresholdTemperature
const success = await this.platform.setTargetTemperature(this.acStates.HeatingThresholdTemperature);
if (success) {
this.acStates.TargetTemperature = this.acStates.HeatingThresholdTemperature;
// Update caches with new values
this.updateCharacteristicCache('TargetTemperature', this.acStates.TargetTemperature);
this.updateCharacteristicCache('HeatingThresholdTemperature', this.acStates.HeatingThresholdTemperature);
this.updateCharacteristicCache('CoolingThresholdTemperature', this.acStates.CoolingThresholdTemperature);
this.platform.log.info(`Auto mode: Successfully set target temperature to ${this.acStates.HeatingThresholdTemperature}°C`);
}
else {
this.platform.log.error(`Auto mode: Failed to set target temperature to ${this.acStates.HeatingThresholdTemperature}°C`);
// Don't update local state if API call failed
}
}
}
/**
* Handle "GET" requests from HomeKit for CoolingThresholdTemperature
*/
async getCoolingThresholdTemperature() {
const cacheKey = 'CoolingThresholdTemperature';
const now = Date.now();
// Check if we have a valid cache for this characteristic
if (this.characteristicCaches[cacheKey] &&
(now - this.characteristicCaches[cacheKey].timestamp) < this.CHARACTERISTIC_CACHE_DURATION) {
const cachedValue = this.characteristicCaches[cacheKey].value;
this.platform.log.debug(`Get Characteristic CoolingThresholdTemperature -> ${cachedValue}°C (cached)`);
return cachedValue;
}
const status = await this.getDeviceStatus();
if (status) {
const targetTemperature = status.components.main.thermostatCoolingSetpoint.coolingSetpoint.value;
const acMode = status.components.main.airConditionerMode.airConditionerMode.value;
const homeKitMode = this.samsungModeToHomeKit(acMode);
let coolingThreshold;
// For Auto mode, set CoolingThresholdTemperature to HeatingThresholdTemperature + 4°C (max 30°C)
if (homeKitMode === 3) { // Auto mode
coolingThreshold = Math.min(targetTemperature + 4, 30);
this.platform.log.debug(`Auto mode: CoolingThresholdTemperature=${coolingThreshold}°C (HeatingThresholdTemperature + 4°C)`);
}
else {
coolingThreshold = targetTemperature;
this.platform.log.debug(`Get Characteristic CoolingThresholdTemperature -> ${targetTemperature}°C`);
}
this.acStates.CoolingThresholdTemperature = coolingThreshold;
// Cache the result
this.characteristicCaches[cacheKey] = {
value: coolingThreshold,
timestamp: now,
};
return coolingThreshold;
}
// Fallback to cached state if API fails
this.platform.log.debug(`Get Characteristic CoolingThresholdTemperature -> ${this.acStates.CoolingThresholdTemperature}°C (fallback)`);
return this.acStates.CoolingThresholdTemperature;
}
/**
* Handle "SET" requests from HomeKit for HeatingThresholdTemperature
*/
async setHeatingThresholdTemperature(value) {
const temperature = value;
// Validate temperature range
if (temperature < 18 || temperature > 30) {
this.platform.log.warn(`Heating temperature ${temperature} is out of range (18-30). Clamping to valid range.`);
this.acStates.HeatingThresholdTemperature = Math.max(18, Math.min(30, temperature));
}
else {
this.acStates.HeatingThresholdTemperature = temperature;
}
this.platform.log.debug(`Set Characteristic HeatingThresholdTemperature -> ${this.acStates.HeatingThresholdTemperature}°C`);
// If in Auto mode, also update CoolingThresholdTemperature to HeatingThresholdTemperature + 4°C (max 30°C)
if (this.acStates.TargetHeatingCoolingState === 3) {
const coolingThreshold = Math.min(this.acStates.HeatingThresholdTemperature + 4, 30);
this.acStates.CoolingThresholdTemperature = coolingThreshold;
this.platform.log.debug(`Auto mode: Updated CoolingThresholdTemperature to ${coolingThreshold}°C (HeatingThresholdTemperature + 4°C)`);
}
// Send command to SmartThings API to set target temperature
const success = await this.platform.setTargetTemperature(this.acStates.HeatingThresholdTemperature);
if (success) {
this.acStates.TargetTemperature = this.acStates.HeatingThresholdTemperature;
// Update caches with new values
this.updateCharacteristicCache('TargetTemperature', this.acStates.TargetTemperature);
this.updateCharacteristicCache('HeatingThresholdTemperature', this.acStates.HeatingThresholdTemperature);
this.updateCharacteristicCache('CoolingThresholdTemperature', this.acStates.CoolingThresholdTemperature);
this.platform.log.info(`Successfully set target temperature to ${this.acStates.HeatingThresholdTemperature}°C`);
}
else {
this.platform.log.error(`Failed to set target temperature to ${this.acStates.HeatingThresholdTemperature}°C`);
// Don't update local state if API call failed
}
}
/**
* Handle "GET" requests from HomeKit for HeatingThresholdTemperature
*/
async getHeatingThresholdTemperature() {
const cacheKey = 'HeatingThresholdTemperature';
const now = Date.now();
// Check if we have a valid cache for this characteristic
if (this.characteristicCaches[cacheKey] &&
(now - this.characteristicCaches[cacheKey].timestamp) < this.CHARACTERISTIC_CACHE_DURATION) {
const cachedValue = this.characteristicCaches[cacheKey].value;
this.platform.log.debug(`Get Characteristic HeatingThresholdTemperature -> ${cachedValue}°C (cached)`);
return cachedValue;
}
const status = await this.getDeviceStatus();
if (status) {
const targetTemperature = status.components.main.thermostatCoolingSetpoint.coolingSetpoint.value;
this.acStates.HeatingThresholdTemperature = targetTemperature;
// Cache the result
this.characteristicCaches[cacheKey] = {
value: targetTemperature,
timestamp: now,
};
this.platform.log.debug(`Get Characteristic HeatingThresholdTemperature -> ${targetTemperature}°C`);
return targetTemperature;
}
// Fallback to cached state if API fails
this.platform.log.debug(`Get Characteristic HeatingThresholdTemperature -> ${this.acStates.HeatingThresholdTemperature}°C (fallback)`);
return this.acStates.HeatingThresholdTemperature;
}
/**
* Handle "SET" requests from HomeKit for TemperatureDisplayUnits
*/
async setTemperatureDisplayUnits(value) {
this.acStates.TemperatureDisplayUnits = value;
this.platform.log.debug('Set Characteristic TemperatureDisplayUnits ->', value);
}
/**
* Handle "GET" requests from HomeKit for TemperatureDisplayUnits
*/
async getTemperatureDisplayUnits() {
const units = this.acStates.TemperatureDisplayUnits;
this.platform.log.debug('Get Characteristic TemperatureDisplayUnits ->', units);
return units;
}
/**
* Handle "GET" requests from HomeKit for CurrentHumidity
*/
async getCurrentHumidity() {
const cacheKey = 'CurrentHumidity';
const now = Date.now();
// Check if we have a valid cache for this characteristic
if (this.characteristicCaches[cacheKey] &&
(now - this.characteristicCaches[cacheKey].timestamp) < this.CHARACTERISTIC_CACHE_DURATION) {
const cachedValue = this.characteristicCaches[cacheKey].value;
this.platform.log.debug(`Get Characteristic CurrentRelativeHumidity -> ${cachedValue}% (cached)`);
return cachedValue;
}
const status = await this.getDeviceStatus();
if (status) {
const humidity = status.components.main.relativeHumidityMeasurement.humidity.value;
this.acStates.CurrentHumidity = humidity;
// Cache the result
this.characteristicCaches[cacheKey] = {
value: humidity,
timestamp: now,
};
this.platform.log.debug(`Get Characteristic CurrentRelativeHumidity -> ${humidity}%`);
return humidity;
}
// Fallback to cached state if API fails
this.platform.log.debug(`Get Characteristic CurrentRelativeHumidity -> ${this.acStates.CurrentHumidity}% (fallback)`);
return this.acStates.CurrentHumidity;
}
}
//# sourceMappingURL=platformAccessory.js.map