home assistant white light buttons

This commit is contained in:
legop3
2026-04-19 21:34:55 -04:00
parent 73524990af
commit 8fd435b3b4
8 changed files with 102 additions and 19 deletions
+2 -3
View File
@@ -1,6 +1,5 @@
You are The Overseer of the rovers. You are able to see their actions and the chatting of the people using them. You are not able to control them.
Your purpose is to provide entertainment and break the silence.
You are The Overseer of the rovers. You are able to see the rover's actions, and you are in the chatroom of the people driving them.
You are not able to control the people or the rovers.
Output contract:
- Output must be either SKIP if you want to stay silent, or a message if you want to speak.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -11,8 +11,8 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
<title>Multi Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-Cxe8USDb.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BquPV0pG.css">
<script type="module" crossorigin src="/assets/index-BrQIm3tI.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-gFnxuq00.css">
</head>
<body>
<div id="root"></div>
@@ -24,6 +24,7 @@ const triggerConfig = []; // [{ runtimeKey, entityId, action, stateEquals, paylo
const triggerRuntime = new Map(); // triggerId -> { lastFiredAt, lastState, lastChanged, lastUpdated }
const HA_BUTTON_EVENT_TYPE = 'ha.button.action';
const LIGHT_IDLE_OFF_MS = 2 * 60 * 1000;
const DEFAULT_WHITE_KELVIN = 4000;
let connection = null;
let unsubscribeEntities = null;
@@ -463,6 +464,31 @@ async function setLightColor(entityId, rgbColor) {
logger.info('Issued Home Assistant color command', { entityId, rgbColor: normalized });
}
async function setLightWhite(entityId, kelvin = DEFAULT_WHITE_KELVIN) {
if (!enabled) {
throw new Error('Home Assistant not configured');
}
const meta = entityConfig.get(entityId);
if (!meta || meta.type !== 'light') {
throw new Error('Home Assistant light required');
}
if (!connection) {
throw new Error('Home Assistant not connected');
}
const nextKelvin = Number(kelvin);
const normalizedKelvin = Number.isFinite(nextKelvin)
? Math.max(2000, Math.min(6500, Math.round(nextKelvin)))
: DEFAULT_WHITE_KELVIN;
await callService(connection, 'light', 'turn_on', {
entity_id: entityId,
color_temp_kelvin: normalizedKelvin,
});
logger.info('Issued Home Assistant white command', {
entityId,
colorTempKelvin: normalizedKelvin,
});
}
function isLightControlLocked() {
return lightsLockState != null;
}
@@ -611,6 +637,27 @@ io.on('connection', (socket) => {
cb({ error: err.message });
}
});
socket.on('homeAssistant:lightWhite', async ({ entityId } = {}, cb = () => {}) => {
const mode = getMode();
if (
(mode === 'admin' && isAdmin(socket) !== true) ||
(mode === 'lockdown' && isLockdownAdmin(socket) !== true)
) {
return cb({ error: 'Insufficient permissions to control Home Assistant' });
}
if (isLightControlLocked()) {
return cb({ error: 'Room controls are locked' });
}
try {
if (!entityId) throw new Error('entityId required');
await setLightWhite(entityId, haConfig?.whiteKelvin);
cb({ success: true });
} catch (err) {
cb({ error: err.message });
}
});
});
module.exports = {
@@ -620,6 +667,7 @@ module.exports = {
toggleEntity,
setEntityState,
setLightColor,
setLightWhite,
setLightsLockedOn,
toggleLightsLockedOn,
homeAssistantEvents: events,
+39 -6
View File
@@ -84,7 +84,7 @@ function getEntityHue(entity) {
return 0;
}
function EntityRow({ entity, connected, controlsLocked, onToggle, onSetColor }) {
function EntityRow({ entity, connected, controlsLocked, onToggle, onSetColor, onSetWhite }) {
const unavailable = entity.state === 'unavailable' || !entity.available;
const isOn = entity.state === 'on';
const supportsColor = entity.type === 'light' && entity.supportsColor;
@@ -125,6 +125,12 @@ function EntityRow({ entity, connected, controlsLocked, onToggle, onSetColor })
event.stopPropagation();
};
const handleSetWhite = (event) => {
stopPropagation(event);
if (disableColor || !onSetWhite) return;
onSetWhite(entity.id);
};
const displayRgb = supportsColor ? hueToRgb(hueRef.current) : [255, 255, 255];
const displayColor = `rgb(${displayRgb.join(',')})`;
@@ -191,10 +197,35 @@ function EntityRow({ entity, connected, controlsLocked, onToggle, onSetColor })
)}
</div>
{supportsColor && (
<span
className="pointer-events-none absolute right-1 top-1 h-2.5 w-2.5 rounded-full border border-white/60"
style={{ backgroundColor: displayColor }}
/>
<div className="absolute right-1 top-1 flex items-center gap-0.5">
<span
role="button"
tabIndex={disableColor ? -1 : 0}
onClick={handleSetWhite}
onPointerDown={stopPropagation}
onKeyDown={(event) => {
if (disableColor) return;
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleSetWhite(event);
}
}}
title="Set white color temperature"
aria-label={`Set ${entity.name || entity.id} to white`}
aria-disabled={disableColor}
className={`inline-flex items-center justify-center rounded border border-slate-200 bg-white px-1 py-0.5 text-xs font-semibold leading-none text-slate-900 ${
disableColor
? 'cursor-not-allowed opacity-60'
: 'cursor-pointer hover:bg-slate-100'
}`}
>
white
</span>
<span
className="pointer-events-none h-2.5 w-2.5 rounded-full border border-white/60"
style={{ backgroundColor: displayColor }}
/>
</div>
)}
{!supportsColor && (
<div className="self-center text-xs font-semibold text-white/90">
@@ -209,7 +240,8 @@ export default function HomeAssistantControls() {
const {
state: { keymap },
} = useControlSystem();
const { session, homeAssistantToggle, homeAssistantSetLightColor } = useSession();
const { session, homeAssistantToggle, homeAssistantSetLightColor, homeAssistantSetLightWhite } =
useSession();
const ha = session?.homeAssistant;
const entities = useMemo(() => ha?.entities || [], [ha?.entities]);
const lightPolicy = ha?.lightPolicy || null;
@@ -274,6 +306,7 @@ export default function HomeAssistantControls() {
controlsLocked={controlsLocked}
onToggle={homeAssistantToggle}
onSetColor={homeAssistantSetLightColor}
onSetWhite={homeAssistantSetLightWhite}
/>
))}
</div>
+3
View File
@@ -19,6 +19,7 @@ const SessionContext = createContext({
homeAssistantToggle: async () => {},
homeAssistantSetState: async () => {},
homeAssistantSetLightColor: async () => {},
homeAssistantSetLightWhite: async () => {},
setNickname: async () => {},
requestVerification: async () => {},
requestPrivateRoverAccess: async () => {},
@@ -141,6 +142,8 @@ export function SessionProvider({ children }) {
emitWithAck('homeAssistant:setState', { entityId, state }),
homeAssistantSetLightColor: (entityId, rgbColor) =>
emitWithAck('homeAssistant:lightColor', { entityId, rgbColor }),
homeAssistantSetLightWhite: (entityId) =>
emitWithAck('homeAssistant:lightWhite', { entityId }),
setNickname: (nickname) => emitWithAck('nickname:set', { nickname }),
requestVerification: () => emitWithAck('verification:request'),
requestPrivateRoverAccess: (roverId) =>