mirror of
https://github.com/myronblair/jarvis
synced 2026-06-30 17:50:23 -05:00
Add brightness, media player, siren control + 5 scene keywords
- Add scene keywords: good morning work, master bedroom scene, porch lights (3 variants)
- Add Tier 0a: brightness control handler ("dim X to Y%", "set X to Y%")
- Add Tier 0b: media player on/off/pause for Apple TV and Pioneer receiver
- Add Tier 0c: per-camera siren activate/silence via switch
This commit is contained in:
@@ -159,6 +159,14 @@ $sceneKeywords = [
|
||||
'front porch lights' => 'scene.outdoors_front_porch_lights',
|
||||
'porch scene' => 'scene.outdoors_front_porch_lights',
|
||||
'office dawn' => 'scene.office_ocean_dawn',
|
||||
'good morning work' => 'scene.good_morning_work',
|
||||
'morning work' => 'scene.good_morning_work',
|
||||
'work morning' => 'scene.good_morning_work',
|
||||
'master bedroom scene' => 'scene.master_bedroom_new_scene',
|
||||
'bedroom scene' => 'scene.master_bedroom_new_scene',
|
||||
'ceiling fan scene' => 'scene.master_bedroom_new_scene',
|
||||
'porch lights on' => 'scene.outdoors_front_porch_lights',
|
||||
'porch lights' => 'scene.outdoors_front_porch_lights',
|
||||
];
|
||||
|
||||
$msgLower = strtolower(trim($message));
|
||||
@@ -222,6 +230,13 @@ if (!$reply && preg_match('/(turn|switch|put|set)\s+(on|off)/i', $message, $acti
|
||||
$searchMsg = preg_replace('/\b(turn|switch|put|set|the|my|all|please|jarvis|on|off|lights?|lamps?|plugs?|strips?)\b/i', ' ', $msgLower);
|
||||
$searchMsg = trim(preg_replace('/\s+/', ' ', $searchMsg));
|
||||
|
||||
// Empty search means generic light command (e.g. "turn on the lights") — all lights
|
||||
if ($searchMsg === '' && $preferLight) {
|
||||
$bestEid = '__all_lights__';
|
||||
$bestName = 'All lights';
|
||||
$bestScore = 1;
|
||||
}
|
||||
|
||||
foreach ($haEntityMap as $eid => $info) {
|
||||
if (($info['state'] ?? '') === 'unavailable') continue;
|
||||
$nameLower = strtolower($info['name']);
|
||||
@@ -324,6 +339,142 @@ if (!$reply && preg_match('/(is|are|what.s|status|state).*(on|off|light|switch|p
|
||||
}
|
||||
|
||||
|
||||
// ── Tier 0a: Brightness control ──────────────────────────────────────────
|
||||
if (!$reply && preg_match('/\b(dim|brighten|brightness|set.*to\s+\d+\s*%|percent)\b/i', $message) && !empty($haEntityMap)) {
|
||||
$pctMatch = null;
|
||||
if (preg_match('/(\d+)\s*%/', $message, $pm)) {
|
||||
$pctMatch = max(1, min(100, (int)$pm[1]));
|
||||
} elseif (preg_match('/\b(full|max|maximum|hundred)\b/i', $message)) {
|
||||
$pctMatch = 100;
|
||||
} elseif (preg_match('/\b(half|fifty)\b/i', $message)) {
|
||||
$pctMatch = 50;
|
||||
} elseif (preg_match('/\b(low|dim|minimum|min|quarter)\b/i', $message)) {
|
||||
$pctMatch = 20;
|
||||
}
|
||||
|
||||
if ($pctMatch !== null) {
|
||||
$searchMsg = preg_replace('/\b(dim|brighten|brightness|set|the|my|to|at|percent|%|\d+|jarvis|light|lights?)\b/i', ' ', $msgLower);
|
||||
$searchMsg = trim(preg_replace('/\s+/', ' ', $searchMsg));
|
||||
$bestEid = null; $bestScore = 0; $bestName = '';
|
||||
foreach ($haEntityMap as $eid => $info) {
|
||||
if ($info['domain'] !== 'light') continue;
|
||||
if (($info['state'] ?? '') === 'unavailable') continue;
|
||||
$nameLower = strtolower($info['name']);
|
||||
$score = 0;
|
||||
if ($searchMsg === '') { $bestEid = '__all_lights__'; $bestName = 'All lights'; $bestScore = 1; break; }
|
||||
$words = array_filter(explode(' ', $searchMsg));
|
||||
foreach ($words as $w) { if (strlen($w) > 2 && strpos($nameLower, $w) !== false) $score += 10; }
|
||||
if ($score > $bestScore) { $bestScore = $score; $bestEid = $eid; $bestName = $info['name']; }
|
||||
}
|
||||
if ($bestEid && $bestScore >= 0) {
|
||||
$haUrl = defined('HA_URL') ? HA_URL : 'http://10.48.200.97:8123';
|
||||
$haToken = defined('HA_TOKEN') ? HA_TOKEN : '';
|
||||
$brightness = (int)round($pctMatch * 2.55);
|
||||
if ($bestEid === '__all_lights__') {
|
||||
$lightIds = array_keys(array_filter($haEntityMap, fn($e) => $e['domain'] === 'light'));
|
||||
$payload = ['entity_id' => $lightIds, 'brightness' => $brightness];
|
||||
} else {
|
||||
$payload = ['entity_id' => $bestEid, 'brightness' => $brightness];
|
||||
}
|
||||
$ch = curl_init($haUrl . '/api/services/light/turn_on');
|
||||
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_POST=>true,
|
||||
CURLOPT_POSTFIELDS=>json_encode($payload),
|
||||
CURLOPT_HTTPHEADER=>['Authorization: Bearer '.$haToken,'Content-Type: application/json'],
|
||||
CURLOPT_TIMEOUT=>8, CURLOPT_CONNECTTIMEOUT=>3]);
|
||||
$haCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_exec($ch); $haCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
|
||||
if ($haCode === 200) {
|
||||
$label = ($bestEid === '__all_lights__') ? 'All lights' : $bestName;
|
||||
$reply = "{$label} set to {$pctMatch}%, {$userAddr}.";
|
||||
$source = 'ha:brightness';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tier 0b: Media player control ────────────────────────────────────────
|
||||
if (!$reply && preg_match('/\b(apple tv|appletv|pioneer|receiver|tv|television)\b/i', $message)
|
||||
&& preg_match('/\b(turn|switch|power|on|off|pause|play|resume|stop|mute)\b/i', $message) && !empty($haEntityMap)) {
|
||||
$mediaTurnOn = (bool) preg_match('/\b(on|play|resume|start)\b/i', $message);
|
||||
$mediaTurnOff = (bool) preg_match('/\b(off|stop|shutdown|power off)\b/i', $message);
|
||||
$mediaPause = (bool) preg_match('/\b(pause|mute)\b/i', $message);
|
||||
$isAppleTV = (bool) preg_match('/\b(apple.?tv|appletv)\b/i', $message);
|
||||
$isPioneer = (bool) preg_match('/\b(pioneer|receiver)\b/i', $message);
|
||||
$isTv = (bool) preg_match('/\b(tv|television)\b/i', $message);
|
||||
|
||||
$targetEid = null; $targetName = '';
|
||||
if ($isAppleTV) {
|
||||
$targetEid = 'media_player.apple_tv_4k';
|
||||
$targetName = 'Apple TV 4K';
|
||||
} elseif ($isPioneer) {
|
||||
$targetEid = 'media_player.vsx_822_2';
|
||||
$targetName = 'Pioneer Receiver';
|
||||
} elseif ($isTv) {
|
||||
$targetEid = 'media_player.apple_tv_4k';
|
||||
$targetName = 'Apple TV';
|
||||
}
|
||||
|
||||
if ($targetEid) {
|
||||
$haUrl = defined('HA_URL') ? HA_URL : 'http://10.48.200.97:8123';
|
||||
$haToken = defined('HA_TOKEN') ? HA_TOKEN : '';
|
||||
if ($mediaPause) {
|
||||
$svc = 'media_player/media_pause';
|
||||
} elseif ($mediaTurnOff) {
|
||||
$svc = 'media_player/turn_off';
|
||||
} else {
|
||||
$svc = 'media_player/turn_on';
|
||||
}
|
||||
$ch = curl_init($haUrl . '/api/services/' . $svc);
|
||||
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_POST=>true,
|
||||
CURLOPT_POSTFIELDS=>json_encode(['entity_id'=>$targetEid]),
|
||||
CURLOPT_HTTPHEADER=>['Authorization: Bearer '.$haToken,'Content-Type: application/json'],
|
||||
CURLOPT_TIMEOUT=>8, CURLOPT_CONNECTTIMEOUT=>3]);
|
||||
curl_exec($ch); $haCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
|
||||
if ($haCode === 200) {
|
||||
if ($mediaPause) $verb = 'paused';
|
||||
elseif ($mediaTurnOff) $verb = 'powered off';
|
||||
else $verb = 'powered on';
|
||||
$reply = "{$targetName} {$verb}, {$userAddr}.";
|
||||
$source = 'ha:media';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tier 0c: Camera siren toggle ─────────────────────────────────────────
|
||||
if (!$reply && preg_match('/\b(siren|alarm|honk|sound (the )?alarm)\b/i', $message)
|
||||
&& preg_match('/\b(camera|back yard|backyard|carport|driveway|front yard)\b/i', $message)) {
|
||||
$sirenOn = !preg_match('/\b(off|stop|disable|silence)\b/i', $message);
|
||||
$haService = $sirenOn ? 'turn_on' : 'turn_off';
|
||||
$sirenMap = [
|
||||
'back yard' => ['switch.camera1_siren_on_event_2', 'Back Yard'],
|
||||
'backyard' => ['switch.camera1_siren_on_event_2', 'Back Yard'],
|
||||
'carport' => ['switch.camera1_siren_on_event_3', 'Carport'],
|
||||
'front yard' => ['switch.front_yard_siren_on_event', 'Front Yard'],
|
||||
'driveway' => ['switch.down_hill_siren_on_event', 'Driveway'],
|
||||
];
|
||||
$targetSiren = null; $targetLabel = 'Camera';
|
||||
foreach ($sirenMap as $kw => [$eid, $lbl]) {
|
||||
if (strpos($msgLower, $kw) !== false) { $targetSiren = $eid; $targetLabel = $lbl; break; }
|
||||
}
|
||||
if (!$targetSiren) {
|
||||
$targetSiren = 'switch.camera1_siren_on_event_2';
|
||||
$targetLabel = 'Back Yard';
|
||||
}
|
||||
$haUrl = defined('HA_URL') ? HA_URL : 'http://10.48.200.97:8123';
|
||||
$haToken = defined('HA_TOKEN') ? HA_TOKEN : '';
|
||||
$ch = curl_init($haUrl . '/api/services/switch/' . $haService);
|
||||
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_POST=>true,
|
||||
CURLOPT_POSTFIELDS=>json_encode(['entity_id'=>$targetSiren]),
|
||||
CURLOPT_HTTPHEADER=>['Authorization: Bearer '.$haToken,'Content-Type: application/json'],
|
||||
CURLOPT_TIMEOUT=>8, CURLOPT_CONNECTTIMEOUT=>3]);
|
||||
curl_exec($ch); $haCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
|
||||
if ($haCode === 200) {
|
||||
$verb = $sirenOn ? 'activated' : 'silenced';
|
||||
$reply = "{$targetLabel} camera siren {$verb}, {$userAddr}.";
|
||||
$source = 'ha:siren';
|
||||
}
|
||||
}
|
||||
|
||||
// ── Email + Planner voice intents (action items, create task/appt from email) ─
|
||||
if (!$reply && preg_match('/\b(email|emails|inbox|gmail|outlook|mail|unread|messages)\b/i', $message)) {
|
||||
$lc = strtolower($message);
|
||||
|
||||
Reference in New Issue
Block a user