export function formatEntityValue(entity, useFormattedEntities, hass, localize) { if (!entity || !hass) return null; // If formatting is not enabled, return the state as-is if (!useFormattedEntities) { return entity.state; } // Use Home Assistant's built-in formatting functions if (typeof hass.formatEntityState === 'function') { return hass.formatEntityState(entity); } // Fallback to basic formatting if Home Assistant functions are not available return entity.state; } function formatBinaryState(state, attributes, hass, localize) { const isOn = state.toLowerCase() === 'on'; if (attributes.device_class) { const key = `state.${attributes.device_class}.${isOn ? 'on' : 'off'}`; return hass.localize(`component.binary_sensor.${key}`) || localize(key) || (isOn ? 'On' : 'Off'); } return isOn ? 'On' : 'Off'; } function formatDeviceTrackerState(state, attributes) { const locationName = attributes.location_name || state; if (locationName.toLowerCase() === 'home') { return 'Home'; } else if (locationName.toLowerCase() === 'not_home') { return 'Away'; } else { return locationName.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' '); } } function formatSensorState(state, attributes) { if (isISODateString(state)) { return formatChargingEndTime(state); } if (!isNaN(parseFloat(state))) { // Remove trailing zeros and decimal point if necessary return formatNumberWithCommas(parseFloat(state).toFixed(0)); } return formatGenericState(state); } function formatGenericState(state) { // Convert snake_case to Title Case return state.split('_').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' '); } export async function getIconActiveState(entityId, hass, config) { const state = hass.states[entityId]; if (!state) return false; const stateStr = state.state.toLowerCase(); // Check custom active and inactive states from config const activeState = config.activeState; const inactiveState = config.inactiveState; if (activeState) { if (activeState === 'default') { return isActiveState(stateStr); } else if (activeState.startsWith('template:')) { const templateResp = await (new Promise((resolve, reject) => { hass.connection.subscribeMessage( (msg) => resolve(msg), { type: "render_template", template: activeState.slice(9), } ) })); return templateResp.result === true ? true : false; } else if (activeState.startsWith('attribute:')) { const [, attributeName, attributeValue] = activeState.split(':'); return state.attributes[attributeName] === attributeValue; } else if (activeState.startsWith('option:')) { return stateStr === activeState.split(':')[1].toLowerCase(); } else { return stateStr === activeState.toLowerCase(); } } if (inactiveState) { if (inactiveState === 'default') { return !isActiveState(stateStr); } else if (inactiveState.startsWith('template:')) { const renderResult = await hass.callApi("post", "template", {template: inactiveState.slice(9)}); return renderResult === "True" ? true : false; } else if (inactiveState.startsWith('attribute:')) { const [, attributeName, attributeValue] = inactiveState.split(':'); return state.attributes[attributeName] !== attributeValue; } else if (inactiveState.startsWith('option:')) { return stateStr !== inactiveState.split(':')[1].toLowerCase(); } else { return stateStr !== inactiveState.toLowerCase(); } } // If no custom states are set, use the default behavior return isActiveState(stateStr); } function isActiveState(state) { const activeStates = [ "on", "active", "open", "connected", "running", "true", "1", "home", "above_horizon", "charging", "full", "yes", "online", "present", "armed", "occupied", "unlocked", "playing", "motion", "engaged", "awake", "detected" ]; return activeStates.includes(state); } export function formatBinarySensorState(state, attributes) { return state; } export function isEngineOn(engineOnEntity) { if (!engineOnEntity) return false; const state = engineOnEntity.state.toLowerCase(); const attributes = engineOnEntity.attributes; // Check attributes for 'engine_on' status if (attributes) { for (const [key, value] of Object.entries(attributes)) { if (typeof value === 'string' && value.toLowerCase() === 'on') { return true; } } } // Handle boolean entities if (['on', 'off', 'true', 'false'].includes(state)) { return state === 'on' || state === 'true'; } // Handle numeric entities if (!isNaN(state)) { return parseFloat(state) > 0; } // Handle string-based entities const engineOnStates = ['on', 'running', 'active', 'true']; return engineOnStates.includes(state); } function formatNumberWithCommas(number) { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } function isISODateString(value) { return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/.test(value); } function formatChargingEndTime(isoDateString) { const date = new Date(isoDateString); const hours = date.getHours(); const minutes = date.getMinutes(); const ampm = hours >= 12 ? "PM" : "AM"; const formattedHours = hours % 12 || 12; const formattedMinutes = minutes < 10 ? `0${minutes}` : minutes; return `${formattedHours}:${formattedMinutes} ${ampm}`; }