Calendar
View and manage Home Assistant calendar events with read, create, update, and delete operations.
As of November 2025, Home Assistant does not yet provide calendar.delete_event or calendar.update_event services. Only calendar.create_event and calendar.get_events are currently available. The deleteEvent and updateEvent methods are implemented according to the planned API structure and will work once Home Assistant adds these services. Until then, events can only be created and read, not modified or deleted through the API.
Quick Example
// Component approach
<Calendar entityId="calendar.personal">
{({ hasActiveEvent, currentEvent, startTime, endTime, getEvents, createEvent }) => (
<div>
<h3>Personal Calendar</h3>
{hasActiveEvent && currentEvent && (
<div>
<p>Current Event: {currentEvent.summary}</p>
{startTime && <p>Started: {new Date(startTime).toLocaleString()}</p>}
{endTime && <p>Ends: {new Date(endTime).toLocaleString()}</p>}
</div>
)}
<button onClick={async () => {
const events = await getEvents(
new Date().toISOString(),
new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
)
console.log('Upcoming events:', events)
}}>
Load Events
</button>
</div>
)}
</Calendar>
// Hook approach
const calendar = useCalendar('calendar.personal')
<div>
<h3>Personal Calendar</h3>
{calendar.hasActiveEvent && calendar.currentEvent && (
<div>
<p>Current Event: {calendar.currentEvent.summary}</p>
{calendar.startTime && <p>Started: {new Date(calendar.startTime).toLocaleString()}</p>}
{calendar.endTime && <p>Ends: {new Date(calendar.endTime).toLocaleString()}</p>}
</div>
)}
</div>
Component API
Basic Usage
import { Calendar } from 'hass-react'
<Calendar entityId="calendar.personal">
{(calendarProps) => (
// Your UI here
)}
</Calendar>
Render Props
The Calendar component provides these props to your render function:
State Properties
hasActiveEvent(boolean) - Whether there's a current or upcoming eventcurrentEvent(CalendarEvent | null) - The current or next eventmessage(string | null) - Event summary/title (shorthand for currentEvent.summary)startTime(string | null) - Event start time in ISO 8601 formatendTime(string | null) - Event end time in ISO 8601 formatlocation(string | null) - Event locationdescription(string | null) - Event descriptionisAllDay(boolean) - Whether the current event is all-day
Support Properties
supportsCreate(boolean) - Calendar supports creating eventssupportsUpdate(boolean) - Calendar supports updating eventssupportsDelete(boolean) - Calendar supports deleting events
Control Methods
getEvents(start: string, end: string): Promise<CalendarEvent[]>- Fetch events in date range (ISO 8601 format)createEvent(event: Omit<CalendarEvent, 'uid'>): Promise<void>- Create a new calendar eventupdateEvent(uid: string, event: Partial<CalendarEvent>, recurrenceRange?: 'THIS_AND_FUTURE' | 'THIS' | 'ALL'): Promise<void>- Update an existing eventdeleteEvent(uid: string, recurrenceRange?: 'THIS_AND_FUTURE' | 'THIS' | 'ALL'): Promise<void>- Delete an event
Calendar Event Structure
interface CalendarEvent {
start: string // Start time (ISO 8601 datetime or date)
end: string // End time (ISO 8601 datetime or date)
summary: string // Event title/summary
uid?: string // Unique identifier
description?: string // Detailed description
location?: string // Event location
rrule?: string // Recurrence rule (RFC 5545)
}
Entity Properties
entityId(string) - The entity IDstate(string) - Raw state value ('on' for active event, 'off' otherwise)attributes(object) - All entity attributeslastChanged(Date) - When the entity last changedlastUpdated(Date) - When the entity was last updated
Hook API
Basic Usage
import { useCalendar } from 'hass-react'
function MyComponent() {
const calendar = useCalendar('calendar.personal')
// All the same properties as component render props
return <div>{calendar.hasActiveEvent ? 'Event in progress' : 'No event'}</div>
}
The useCalendar hook returns an object with all the same properties and methods as the component's render props.
List All Calendars
Use the useCalendars hook to retrieve all available calendar entities:
import { useCalendars } from 'hass-react'
function CalendarList() {
const calendars = useCalendars()
return (
<div>
<h2>Available Calendars ({calendars.length})</h2>
{calendars.map(calendar => (
<div key={calendar.entity_id}>
{calendar.attributes.friendly_name || calendar.entity_id}
</div>
))}
</div>
)
}
The useCalendars hook fetches all calendar entities from Home Assistant and returns an array of calendar objects.
Examples
Display Current Event
<Calendar entityId="calendar.work">
{({ hasActiveEvent, currentEvent, startTime, endTime, location, attributes }) => (
<div>
<h3>{attributes.friendly_name}</h3>
{hasActiveEvent && currentEvent ? (
<div>
<h4>{currentEvent.summary}</h4>
{startTime && <p>Start: {new Date(startTime).toLocaleString()}</p>}
{endTime && <p>End: {new Date(endTime).toLocaleString()}</p>}
{location && <p>Location: {location}</p>}
{currentEvent.description && <p>{currentEvent.description}</p>}
</div>
) : (
<p>No upcoming events</p>
)}
</div>
)}
</Calendar>
List Upcoming Events
<Calendar entityId="calendar.personal">
{({ getEvents, attributes }) => {
const [events, setEvents] = useState<CalendarEvent[]>([])
useEffect(() => {
const fetchEvents = async () => {
const start = new Date().toISOString()
const end = new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString()
const upcomingEvents = await getEvents(start, end)
setEvents(upcomingEvents)
}
fetchEvents()
}, [getEvents])
return (
<div>
<h3>{attributes.friendly_name}</h3>
<h4>Next 14 Days</h4>
{events.length === 0 ? (
<p>No upcoming events</p>
) : (
<ul>
{events.map((event, index) => (
<li key={index}>
<strong>{event.summary}</strong>
<p>{new Date(event.start).toLocaleString()}</p>
{event.location && <p>📍 {event.location}</p>}
</li>
))}
</ul>
)}
</div>
)
}}
</Calendar>
Create New Event
<Calendar entityId="calendar.personal">
{({ createEvent, supportsCreate, attributes }) => {
const [summary, setSummary] = useState('')
const [startDateTime, setStartDateTime] = useState('')
const [endDateTime, setEndDateTime] = useState('')
const handleCreate = async () => {
if (!summary || !startDateTime || !endDateTime) {
alert('Please fill in all fields')
return
}
try {
await createEvent({
summary,
start: startDateTime + ':00', // Add seconds
end: endDateTime + ':00'
})
// Clear form
setSummary('')
setStartDateTime('')
setEndDateTime('')
alert('Event created successfully!')
} catch (error) {
alert(`Failed to create event: ${error}`)
}
}
return (
<div>
<h3>{attributes.friendly_name}</h3>
{supportsCreate ? (
<div>
<input
type="text"
placeholder="Event title"
value={summary}
onChange={(e) => setSummary(e.target.value)}
/>
<input
type="datetime-local"
value={startDateTime}
onChange={(e) => setStartDateTime(e.target.value)}
/>
<input
type="datetime-local"
value={endDateTime}
onChange={(e) => setEndDateTime(e.target.value)}
/>
<button onClick={handleCreate}>Create Event</button>
</div>
) : (
<p>This calendar does not support creating events</p>
)}
</div>
)
}}
</Calendar>
Update and Delete Events
<Calendar entityId="calendar.family">
{({ getEvents, updateEvent, deleteEvent, supportsUpdate, supportsDelete, attributes }) => {
const [events, setEvents] = useState<CalendarEvent[]>([])
const loadEvents = useCallback(async () => {
const start = new Date().toISOString()
const end = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()
const fetchedEvents = await getEvents(start, end)
setEvents(fetchedEvents)
}, [getEvents])
useEffect(() => {
loadEvents()
}, [loadEvents])
const handleUpdateEvent = async (uid: string, newSummary: string) => {
try {
await updateEvent(uid, { summary: newSummary })
await loadEvents()
} catch (error) {
alert(`Failed to update event: ${error}`)
}
}
const handleDeleteEvent = async (uid: string) => {
if (confirm('Delete this event?')) {
try {
await deleteEvent(uid)
await loadEvents()
} catch (error) {
alert(`Failed to delete event: ${error}`)
}
}
}
return (
<div>
<h3>{attributes.friendly_name}</h3>
{events.length === 0 ? (
<p>No upcoming events</p>
) : (
<ul>
{events.map((event, index) => (
<li key={index}>
<strong>{event.summary}</strong>
<p>{new Date(event.start).toLocaleString()}</p>
{event.location && <p>📍 {event.location}</p>}
{supportsUpdate && event.uid && (
<button onClick={() => {
const newSummary = prompt('New title:', event.summary)
if (newSummary) handleUpdateEvent(event.uid!, newSummary)
}}>
Edit
</button>
)}
{supportsDelete && event.uid && (
<button onClick={() => handleDeleteEvent(event.uid!)}>
Delete
</button>
)}
</li>
))}
</ul>
)}
</div>
)
}}
</Calendar>
Full Calendar Manager
<Calendar entityId="calendar.family">
{({
hasActiveEvent,
currentEvent,
startTime,
getEvents,
createEvent,
deleteEvent,
supportsCreate,
supportsDelete,
attributes
}) => {
const [events, setEvents] = useState<CalendarEvent[]>([])
const [showCreateForm, setShowCreateForm] = useState(false)
const [newEvent, setNewEvent] = useState({
summary: '',
start: '',
end: '',
location: '',
description: ''
})
const loadEvents = useCallback(async () => {
const start = new Date().toISOString()
const end = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString()
const fetchedEvents = await getEvents(start, end)
setEvents(fetchedEvents)
}, [getEvents])
useEffect(() => {
loadEvents()
}, [loadEvents])
const handleCreateEvent = async () => {
if (!newEvent.summary || !newEvent.start || !newEvent.end) {
alert('Please fill in required fields')
return
}
try {
await createEvent({
summary: newEvent.summary,
start: newEvent.start + ':00',
end: newEvent.end + ':00',
location: newEvent.location || undefined,
description: newEvent.description || undefined
})
setNewEvent({ summary: '', start: '', end: '', location: '', description: '' })
setShowCreateForm(false)
await loadEvents()
} catch (error) {
alert(`Failed to create event: ${error}`)
}
}
const handleDeleteEvent = async (uid: string) => {
if (confirm('Delete this event?')) {
try {
await deleteEvent(uid)
await loadEvents()
} catch (error) {
alert(`Failed to delete event: ${error}`)
}
}
}
return (
<div>
<h3>{attributes.friendly_name}</h3>
{/* Current Event */}
{hasActiveEvent && currentEvent && (
<div className="current-event">
<h4>Current/Next Event</h4>
<p><strong>{currentEvent.summary}</strong></p>
{startTime && <p>Start: {new Date(startTime).toLocaleString()}</p>}
</div>
)}
{/* Create Event Form */}
{supportsCreate && (
<div>
{!showCreateForm ? (
<button onClick={() => setShowCreateForm(true)}>
+ Create Event
</button>
) : (
<div className="create-form">
<h4>New Event</h4>
<input
type="text"
placeholder="Event title *"
value={newEvent.summary}
onChange={(e) => setNewEvent({ ...newEvent, summary: e.target.value })}
/>
<input
type="datetime-local"
value={newEvent.start}
onChange={(e) => setNewEvent({ ...newEvent, start: e.target.value })}
/>
<input
type="datetime-local"
value={newEvent.end}
onChange={(e) => setNewEvent({ ...newEvent, end: e.target.value })}
/>
<input
type="text"
placeholder="Location (optional)"
value={newEvent.location}
onChange={(e) => setNewEvent({ ...newEvent, location: e.target.value })}
/>
<textarea
placeholder="Description (optional)"
value={newEvent.description}
onChange={(e) => setNewEvent({ ...newEvent, description: e.target.value })}
/>
<button onClick={handleCreateEvent}>Create</button>
<button onClick={() => {
setShowCreateForm(false)
setNewEvent({ summary: '', start: '', end: '', location: '', description: '' })
}}>
Cancel
</button>
</div>
)}
</div>
)}
{/* Event List */}
<div>
<h4>Upcoming Events</h4>
{events.length === 0 ? (
<p>No upcoming events</p>
) : (
<ul>
{events.map((event, index) => (
<li key={index}>
<p><strong>{event.summary}</strong></p>
<p>{new Date(event.start).toLocaleString()}</p>
{event.location && <p>📍 {event.location}</p>}
{event.description && <p>{event.description}</p>}
{supportsDelete && event.uid && (
<button onClick={() => handleDeleteEvent(event.uid!)}>
Delete
</button>
)}
</li>
))}
</ul>
)}
</div>
</div>
)
}}
</Calendar>
Using Hooks
import { useCalendar } from 'hass-react'
function CalendarWidget({ entityId }) {
const calendar = useCalendar(entityId)
const [upcomingEvents, setUpcomingEvents] = useState<CalendarEvent[]>([])
useEffect(() => {
const fetchEvents = async () => {
try {
const start = new Date().toISOString()
const end = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
const events = await calendar.getEvents(start, end)
setUpcomingEvents(events.slice(0, 5))
} catch (error) {
console.error('Failed to fetch events:', error)
}
}
fetchEvents()
}, [calendar.getEvents])
return (
<div>
<h3>{calendar.attributes.friendly_name}</h3>
{/* Current Event */}
{calendar.hasActiveEvent && calendar.currentEvent && (
<div className="current-event">
<h4>Now</h4>
<p><strong>{calendar.currentEvent.summary}</strong></p>
{calendar.startTime && (
<p>Started: {new Date(calendar.startTime).toLocaleTimeString()}</p>
)}
{calendar.endTime && (
<p>Until: {new Date(calendar.endTime).toLocaleTimeString()}</p>
)}
{calendar.location && <p>📍 {calendar.location}</p>}
</div>
)}
{/* Upcoming Events */}
<div>
<h4>Coming Up</h4>
{upcomingEvents.length === 0 ? (
<p>No events this week</p>
) : (
<ul>
{upcomingEvents.map((event, index) => (
<li key={index}>
<strong>{event.summary}</strong>
<p>
{new Date(event.start).toLocaleDateString()} at{' '}
{new Date(event.start).toLocaleTimeString()}
</p>
</li>
))}
</ul>
)}
</div>
{/* Capabilities */}
<p>
<small>
Capabilities:
{calendar.supportsCreate && ' Create'}
{calendar.supportsUpdate && ' Update'}
{calendar.supportsDelete && ' Delete'}
</small>
</p>
<p>
<small>Last updated: {calendar.lastUpdated.toLocaleTimeString()}</small>
</p>
</div>
)
}
Notes
- All datetime values must be in ISO 8601 format (e.g.,
2024-01-15T10:00:00) - For all-day events, use date format (e.g.,
2024-01-15) - When using
datetime-localinputs, append:00for seconds (e.g.,value + ':00') - The
getEventsmethod requiresstartandendparameters in ISO 8601 format - Not all calendars support create, update, or delete operations - check the support properties
- Recurring events use the
rruleproperty following RFC 5545 format - When updating or deleting recurring events, use
recurrenceRangeto specify the scope:'THIS'- Only this occurrence'THIS_AND_FUTURE'- This and all future occurrences'ALL'- All occurrences