Add support for 'fan_ctrl' service

This commit is contained in:
Adrian Jagielak 2025-07-23 23:19:06 +02:00
parent 63533c0199
commit 661f297b1f
No known key found for this signature in database
GPG Key ID: 0818CF7AF6C62BFB
4 changed files with 82 additions and 3 deletions

View File

@ -62,7 +62,7 @@ todo periodical refresh of state
| door_lock | | | | door_lock | | |
| doorman | | | | doorman | | |
| fan | | | | fan | | |
| fan_ctrl | | | | fan_ctrl | | |
| fire_detector | | | | fire_detector | | |
| garage_door | | | | garage_door | | |
| gas_detector | | | | gas_detector | | |

View File

@ -1,6 +1,6 @@
# https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config # https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config
name: Futurehome name: Futurehome
version: "0.0.21" version: "0.0.22"
slug: futurehome slug: futurehome
description: Local Futurehome Smarthub integration description: Local Futurehome Smarthub integration
url: "https://github.com/adrianjagielak/home-assistant-futurehome" url: "https://github.com/adrianjagielak/home-assistant-futurehome"

View File

@ -3,6 +3,7 @@ import { VinculumPd7Device, VinculumPd7Service } from "../fimp/vinculum_pd7_devi
import { log } from "../logger"; import { log } from "../logger";
import { basic__components } from "../services/basic"; import { basic__components } from "../services/basic";
import { battery__components } from "../services/battery"; import { battery__components } from "../services/battery";
import { fan_ctrl__components } from "../services/fan_ctrl";
import { out_bin_switch__components } from "../services/out_bin_switch"; import { out_bin_switch__components } from "../services/out_bin_switch";
import { out_lvl_switch__components } from "../services/out_lvl_switch"; import { out_lvl_switch__components } from "../services/out_lvl_switch";
import { scene_ctrl__components } from "../services/scene_ctrl"; import { scene_ctrl__components } from "../services/scene_ctrl";
@ -81,7 +82,7 @@ type HaDeviceConfig = {
qos: number, qos: number,
} }
export type HaComponent = SensorComponent | BinarySensorComponent | SwitchComponent | NumberComponent | ClimateComponent | SelectComponent; export type HaComponent = SensorComponent | BinarySensorComponent | SwitchComponent | NumberComponent | ClimateComponent | SelectComponent | FanComponent;
// Device class supported values: https://www.home-assistant.io/integrations/homeassistant/#device-class // Device class supported values: https://www.home-assistant.io/integrations/homeassistant/#device-class
@ -160,6 +161,20 @@ export type SelectComponent = {
value_template: string; value_template: string;
} }
/// https://www.home-assistant.io/integrations/fan.mqtt/
export type FanComponent = {
unique_id: string;
// platform
p: 'fan';
command_topic: string;
optimistic: boolean;
preset_modes: string[];
preset_mode_command_topic: string;
preset_mode_state_template: string;
state_value_template: string;
preset_mode_value_template: string;
}
export type ServiceComponentsCreationResult = { export type ServiceComponentsCreationResult = {
components: { [key: string]: HaComponent }; components: { [key: string]: HaComponent };
commandHandlers?: CommandHandlers; commandHandlers?: CommandHandlers;
@ -172,6 +187,7 @@ const serviceHandlers: {
} = { } = {
basic: basic__components, basic: basic__components,
battery: battery__components, battery: battery__components,
fan_ctrl: fan_ctrl__components,
out_bin_switch: out_bin_switch__components, out_bin_switch: out_bin_switch__components,
out_lvl_switch: out_lvl_switch__components, out_lvl_switch: out_lvl_switch__components,
scene_ctrl: scene_ctrl__components, scene_ctrl: scene_ctrl__components,

View File

@ -0,0 +1,63 @@
import { sendFimpMsg } from "../fimp/fimp";
import { VinculumPd7Device, VinculumPd7Service } from "../fimp/vinculum_pd7_device";
import { ServiceComponentsCreationResult } from "../ha/publish_device";
export function fan_ctrl__components(
topicPrefix: string,
device: VinculumPd7Device,
svc: VinculumPd7Service
): ServiceComponentsCreationResult | undefined {
const supModes: string[] = svc.props?.sup_modes ?? [];
if (!supModes.length) return undefined; // nothing useful to expose
const commandTopic = `${topicPrefix}${svc.addr}/command`;
return {
components: {
[svc.addr]: {
unique_id: svc.addr,
p: "fan",
command_topic: commandTopic,
optimistic: true,
preset_modes: supModes,
preset_mode_command_topic: commandTopic,
preset_mode_state_template: `{{ value_json['${svc.addr}'].mode }}`,
// Fan is considered "on" if mode is not off/stop
state_value_template: `{{ 'ON' if value_json['${svc.addr}'].mode not in ['off', 'stop'] else 'OFF' }}`,
preset_mode_value_template: `{{ value_json['${svc.addr}'].mode }}`,
},
},
commandHandlers: {
[commandTopic]: async (payload: string) => {
// Handle both on/off commands and preset mode commands
if (payload === 'ON' || payload === 'OFF') {
// For simple on/off, use the first available mode for "on"
const mode = payload === 'ON' ? supModes[0] : 'off';
if (supModes.includes(mode) || mode === 'off') {
await sendFimpMsg({
address: svc.addr!,
service: "fan_ctrl",
cmd: "cmd.mode.set",
val: mode,
val_t: "string",
});
}
} else {
// Treat as preset mode command
if (supModes.includes(payload)) {
await sendFimpMsg({
address: svc.addr!,
service: "fan_ctrl",
cmd: "cmd.mode.set",
val: payload,
val_t: "string",
});
}
}
},
},
};
}