homebridge-smartthings icon indicating copy to clipboard operation
homebridge-smartthings copied to clipboard

Set dishwasher as a faucet device

Open Damien514 opened this issue 2 years ago • 2 comments

Is your feature request related to a problem? Please describe: My dishwasher is displayed as a switch, and I can choose light or fan as an option. Would it be possible to have a faucet icon instead (that what I have for my LG washing machine)

Describe the solution you'd like: Define the type Faucet / water device when the device is a dishwasher.

Describe alternatives you've considered: Alternative is keeping as is, not a bug or a big deal.

Additional context: As a Faucet, it will be displayed on the top status block in the Water category.

Damien514 avatar May 25 '23 15:05 Damien514

2 years later, I asked Claude to make this change, and it's perfect. I am not really into GIT, so no idea how to pull theses changes. If anybody wants to add the changes in the rep, that would be awesome (for future updates). Thanks!

MultiServiceAccessory.js

"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiServiceAccessory = void 0; const basePlatformAccessory_1 = require("./basePlatformAccessory"); const motionService_1 = require("./services/motionService"); const batteryService_1 = require("./services/batteryService"); const temperatureService_1 = require("./services/temperatureService"); const humidityService_1 = require("./services/humidityService"); const lightSensorService_1 = require("./services/lightSensorService"); const contactSensorService_1 = require("./services/contactSensorService"); const lockService_1 = require("./services/lockService"); const doorService_1 = require("./services/doorService"); const switchService_1 = require("./services/switchService"); const lightService_1 = require("./services/lightService"); const fanSwitchLevelService_1 = require("./services/fanSwitchLevelService"); const occupancySensorService_1 = require("./services/occupancySensorService"); const leakDetector_1 = require("./services/leakDetector"); const smokeDetector_1 = require("./services/smokeDetector"); const carbonMonoxideDetector_1 = require("./services/carbonMonoxideDetector"); const valveService_1 = require("./services/valveService"); const fanSpeedService_1 = require("./services/fanSpeedService"); const windowCoveringService_1 = require("./services/windowCoveringService"); const thermostatService_1 = require("./services/thermostatService"); const dishwasherValveService_1 = require("./services/dishwasherValveService");

/**

  • 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. / class MultiServiceAccessory extends basePlatformAccessory_1.BasePlatformAccessory { constructor(platform, accessory, capabilities) { super(platform, accessory); /* * These are just used to create a working example * You should implement your own code to track the state of your accessory */ this.services = []; this.capabilities = capabilities; // Add services per capabilities // If this device has a 'switch' or 'thermostatMode' capability, need to look at the combinations to // determine what kind of device. Fans, lights, // switches all have a switch capability and we need to add the correct one. Object.keys(MultiServiceAccessory.capabilityMap).forEach((capability) => { if (capabilities.find((c) => c.id === capability)) { this.services.push(new (MultiServiceAccessory.capabilityMap[capability])(this.platform, this.accessory, [capability], this, this.name, this.deviceStatus)); } }); if (capabilities.find(c => (c.id === 'switch') || c.id === 'thermostatMode')) { let service = this.findComboService(capabilities); if (service === undefined) { service = switchService_1.SwitchService; }

         if (capabilities.find(c => c.id === 'dishwasherOperatingState') && service === switchService_1.SwitchService) {
             this.log.debug(`Service Switch ignoré pour ${this.name} (lave-vaisselle détecté)`);
             return; // Ne pas ajouter le service Switch
         }
    
         this.services.push(new service(this.platform, this.accessory, capabilities.map(c => c.id), this, this.name, this.deviceStatus));
     }
    

    } // If this is a capability that needs to be combined with others in order to determone the service, // go through the combinations of cabailities in the map and return the first matching service. // We look at combinations because devices like lights that dim also have switch capabilities // as well as switchlevel capabilities. Fans have switch and fanlevel capabilities. This allows // us to find a services that best matches the combination of capabilities reported by the device. findComboService(deviceCapabilities) { let service = undefined; MultiServiceAccessory.comboCapabilityMap.forEach(entry => { if (service === undefined) { let found = true; entry.capabilities.forEach(c => { if (!deviceCapabilities.find(dc => dc.id === c)) { found = false; } }); if (found) { service = entry.service; } } }); return service; } isOnline() { return this.online; } // Find return if a capability is supported by the multi-service accessory static capabilitySupported(capability) { if (Object.keys(MultiServiceAccessory.capabilityMap).find(c => c === capability) || capability === 'switch') { return true; } else { return false; } } async refreshStatus() { return super.refreshStatus(); } startPollingState(pollSeconds, getValue, service, chracteristic, targetStateCharacteristic, getTargetState) { return super.startPollingState(pollSeconds, getValue, service, chracteristic, targetStateCharacteristic, getTargetState); } processEvent(event) { this.log.debug(Received events for ${this.name}); const service = this.services.find(s => s.capabilities.find(c => c === event.capability)); if (service) { service.processEvent(event); } } } exports.MultiServiceAccessory = MultiServiceAccessory; // Order of these matters. Make sure secondary capabilities like 'battery' and 'contactSensor' are at the end. MultiServiceAccessory.capabilityMap = { 'doorControl': doorService_1.DoorService, 'lock': lockService_1.LockService, // 'switch': SwitchService, 'windowShadeLevel': windowCoveringService_1.WindowCoveriingService, 'motionSensor': motionService_1.MotionService, 'waterSensor': leakDetector_1.LeakDetectorService, 'smokeDetector': smokeDetector_1.SmokeDetectorService, 'carbonMonoxideDetector': carbonMonoxideDetector_1.CarbonMonoxideDetectorService, 'presenceSensor': occupancySensorService_1.OccupancySensorService, 'temperatureMeasurement': temperatureService_1.TemperatureService, 'relativeHumidityMeasurement': humidityService_1.HumidityService, 'illuminanceMeasurement': lightSensorService_1.LightSensorService, 'contactSensor': contactSensorService_1.ContactSensorService, 'battery': batteryService_1.BatteryService, 'valve': valveService_1.ValveService, 'dishwasherOperatingState': dishwasherValveService_1.DishwasherValveService, }; // Maps combinations of supported capabilities to a service MultiServiceAccessory.comboCapabilityMap = [ { capabilities: ['switch', 'dishwasherOperatingState'], service: dishwasherValveService_1.DishwasherValveService, }, { capabilities: ['switch', 'fanSpeed', 'switchLevel'], service: fanSwitchLevelService_1.FanSwitchLevelService, }, { capabilities: ['switch', 'fanSpeed'], service: fanSpeedService_1.FanSpeedService, }, { capabilities: ['switch', 'switchLevel'], service: lightService_1.LightService, }, { capabilities: ['switch', 'colorControl'], service: lightService_1.LightService, }, { capabilities: ['switch', 'colorTemperature'], service: lightService_1.LightService, }, { capabilities: ['switch', 'valve'], service: valveService_1.ValveService, }, { capabilities: ['switch'], service: switchService_1.SwitchService, }, { capabilities: ['temperatureMeasurement', 'thermostatMode', 'thermostatHeatingSetpoint', 'thermostatCoolingSetpoint'], service: thermostatService_1.ThermostatService, }, ]; //# sourceMappingURL=multiServiceAccessory.js.map

dishwasherValveService.js

"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DishwasherValveService = void 0; const baseService_1 = require("./baseService");

class DishwasherValveService extends baseService_1.BaseService { constructor(platform, accessory, capabilities, multiServiceAccessory, name, deviceStatus) { super(platform, accessory, capabilities, multiServiceAccessory, name, deviceStatus);

	this.setServiceType(platform.Service.Valve);

	this.log.debug(`Ajout du DishwasherValveService pour ${this.name}`);

	this.service.getCharacteristic(platform.Characteristic.Active)
		.onGet(this.getValveState.bind(this))
		.onSet(this.setValveState.bind(this));

	this.service.getCharacteristic(platform.Characteristic.ValveType)
		.onGet(() => platform.Characteristic.ValveType.GENERIC_VALVE);

	this.service.getCharacteristic(platform.Characteristic.InUse)
		.onGet(this.getValveState.bind(this));

	// Polling pour surveiller l'état
	this.multiServiceAccessory.startPollingState(
		this.platform.config.PollingInterval || 30,
		this.getValveState.bind(this),
		this.service,
		platform.Characteristic.Active
	);
}

async getValveState() {
	return new Promise((resolve, reject) => {
		this.getStatus().then(success => {
			if (!success || !this.multiServiceAccessory.isOnline()) {
				// Au lieu de rejeter ou signaler offline, retourner valve fermée
				this.log.debug(`${this.name} hors ligne ou inaccessible - valve fermée`);
				resolve(this.platform.Characteristic.Active.INACTIVE);
				return;
			}

			const operatingState = this.deviceStatus.status.dishwasherOperatingState?.machineState?.value;
			const switchState = this.deviceStatus.status.switch?.switch?.value;

			this.log.debug(`État du lave-vaisselle ${this.name}: operatingState=${operatingState}, switch=${switchState}`);

			const isRunning = operatingState === 'run' ||
							operatingState === 'prewash' ||
							operatingState === 'wash' ||
							operatingState === 'rinse' ||
							operatingState === 'spin' ||
							operatingState === 'drying';

			const isActive = (switchState === 'on' && isRunning) ?
				this.platform.Characteristic.Active.ACTIVE :
				this.platform.Characteristic.Active.INACTIVE;

			this.log.debug(`Valve ${this.name} ${isActive ? 'ouverte' : 'fermée'}`);
			resolve(isActive);
		});
	});
}

async setValveState(value) {
	this.log.debug(`Commande setValveState(${value}) pour ${this.name}`);

	// Même si offline, on essaie quand même la commande
	// HomeKit montrera l'état réel après tentative
	const command = value === this.platform.Characteristic.Active.ACTIVE ? 'on' : 'off';

	this.multiServiceAccessory.sendCommand('switch', command).then((success) => {
		if (success) {
			this.log.debug(`Commande ${command} réussie pour ${this.name}`);
			this.deviceStatus.timestamp = 0;
		} else {
			this.log.debug(`Commande ${command} échouée pour ${this.name} - device probablement offline`);
			// Pas d'erreur HapStatusError, HomeKit gèrera l'état
		}
	});
}

processEvent(event) {
	this.log.debug(`Événement reçu pour ${this.name}: ${event.capability} = ${event.value}`);

	if (event.capability === 'dishwasherOperatingState' || event.capability === 'switch') {
		this.getValveState().then(valveState => {
			this.service.updateCharacteristic(this.platform.Characteristic.Active, valveState);
			this.service.updateCharacteristic(this.platform.Characteristic.InUse, valveState);
		});
	}
}

}

exports.DishwasherValveService = DishwasherValveService;

dishwasherValveService.d.ts

import { PlatformAccessory, CharacteristicValue } from 'homebridge'; import { IKHomeBridgeHomebridgePlatform } from '../platform'; import { BaseService } from './baseService'; import { MultiServiceAccessory } from '../multiServiceAccessory'; import { ShortEvent } from '../webhook/subscriptionHandler';

export declare class DishwasherValveService extends BaseService { constructor(platform: IKHomeBridgeHomebridgePlatform, accessory: PlatformAccessory, capabilities: string[], multiServiceAccessory: MultiServiceAccessory, name: string, deviceStatus: any);

getValveState(): Promise<number>;
setValveState(value: CharacteristicValue): Promise<void>;
processEvent(event: ShortEvent): void;

}

Damien514 avatar Jul 17 '25 14:07 Damien514

create a pull request

pizzapi2012 avatar Sep 10 '25 16:09 pizzapi2012