LaMetric Time Local Notifier - Easy Local LAN Notifications (Icon and sound support)
Hi everyone!
I wanted to share a custom device driver I’ve been working on (with AI’s help) to bring fully local, notifications from Hubitat to the LaMetric Time smart clock.
Unlike the ported Smart-things driver that uses a complex connector setup, this driver interfaces directly with LaMetric’s Local LAN Device API v2. It requires zero cloud configuration, operates entirely within your home network, and implements Hubitat's standard Notification capability.
Core Features
-
Longer notifications allowed: The Smartthings port limited how long a notification could be. This driver allows up to the limit of the device which I understand is 32K.
-
Dynamic Icon Switching: Set your icon on the fly directly inside your notification using bracket prefixes like
[i1234](for static images) or[a5266](for animated movies). -
Built-in 44-Sound Catalog: A pre-populated dropdown menu in the UI maps all 44 factory-installed LaMetric audio assets—plus inline overrides like
[sound:cash].
Prerequisites: Getting Your LaMetric API Key
Before installing the driver, you need to grab your local API Key and Lametric Time LAN IP address.
-
Find your IP: Locate your clock's IP address (e.g.,
10.0.0.170) using your router or the LaMetric smartphone app. -
Access the Developer Portal: Go to developer.lametric.com and log in using your LaMetric credentials.
-
Retrieve the Token: Once logged in, look under My Devices or your account profile. You will see a long alphanumeric hash titled API Token or API Key. Copy this string.
The Custom Driver Code
Install this on your hub by navigating to Drivers Code -> Add Driver, pasting the script below, and clicking Save.
groovy
/**
* LaMetric Local Notification Driver for Hubitat (v3.2)
* Features: 44-Sound Catalog, Dynamic Overrides, Animated Canvas Padding, Fixed Regex Order
*/
metadata {
definition (name: "LaMetric Local Notifier", namespace: "custom", author: "Community") {
capability "Notification"
}
preferences {
input name: "deviceIp", type: "text", title: "LaMetric IP Address", required: true, defaultValue: "10.0.0.170"
input name: "apiKey", type: "password", title: "LaMetric API Key", required: true
input name: "iconId", type: "text", title: "Default Icon ID", required: true, defaultValue: "i7080"
input name: "cycles", type: "number", title: "Display Cycles", required: true, defaultValue: 3
input name: "defaultSound", type: "enum", title: "Default Sound Effect", required: true, defaultValue: "none",
options: [
"none": "🔇 No Sound",
// Objects & Vehicles
"bicycle": "🚲 Bicycle Bell", "car": "🚗 Retro Car Horn", "cash": "💰 Cash Register",
"letter_email": "✉️ Email Notification", "open_door": "🚪 Door Opening", "statistic": "📈 Statistic Rise",
// Nature & Environment
"thunder": "⚡ Thunder", "water1": "💧 Water Drop 1", "water2": "🌊 Water Drip 2",
"wind": "💨 Wind Long", "wind_short": "🍃 Wind Short",
// Animals
"cat": "🐱 Cat Meow", "dog": "🐶 Dog Bark", "dog2": "🐕 Dog Bark 2",
// Chimes & FX
"energy": "🔋 Energy Chime", "knock-knock": "✊ Knock Knock",
"notification": "🔔 Generic Chime 1", "notification2": "🔔 Generic Chime 2",
"notification3": "🔔 Generic Chime 3", "notification4": "🔔 Generic Chime 4",
// Gaming / Achievements
"win": "🏆 Victory Win 1", "win2": "🎮 Victory Win 2",
"lose1": "📉 Fail Game Over 1", "lose2": "👾 Fail Game Over 2",
// Positive Tone Responses
"positive1": "✅ Positive Tone 1", "positive2": "✅ Positive Tone 2", "positive3": "✅ Positive Tone 3",
"positive4": "✅ Positive Tone 4", "positive5": "✅ Positive Tone 5", "positive6": "✅ Positive Tone 6",
// Negative Tone Responses
"negative1": "❌ Negative Tone 1", "negative2": "❌ Negative Tone 2", "negative3": "❌ Negative Tone 3",
"negative4": "❌ Negative Tone 4", "negative5": "❌ Negative Tone 5",
// Hardware Alarms
"alarm1": "⏰ System Alarm 1", "alarm2": "⏰ System Alarm 2", "alarm3": "⏰ System Alarm 3",
"alarm4": "⏰ System Alarm 4", "alarm5": "⏰ System Alarm 5", "alarm6": "⏰ System Alarm 6",
"alarm7": "⏰ System Alarm 7", "alarm8": "⏰ System Alarm 8", "alarm9": "⏰ System Alarm 9",
"alarm10": "⏰ System Alarm 10", "alarm11": "⏰ System Alarm 11", "alarm12": "⏰ System Alarm 12", "alarm13": "⏰ System Alarm 13"
]
}
}
def deviceNotification(String text) {
sendLaMetricNotification(text)
}
def sendLaMetricNotification(String msg) {
// 1. PREFIX PARSING: Icon Extraction via flexible inclusion find()
def activeIcon = settings.iconId
def iconMatcher = (msg =~ /\[([iI|aA][A-Za-z0-9]+)\]/)
if (iconMatcher.find()) {
activeIcon = iconMatcher[0][1] // Extract the alphanumeric ID
msg = msg.replaceAll(/\[[iI|aA][A-Za-z0-9]+\]/, "").trim() // Thoroughly strip the bracket token out
} else {
msg = msg.trim()
}
// 2. PREFIX PARSING: Sound Extraction -> [sound:XXXX]
def activeSound = settings.defaultSound
def soundMatcher = (msg =~ /\[sound:([^\]]+)\]/)
if (soundMatcher.find()) {
activeSound = soundMatcher[0][1].trim()
msg = msg.replaceAll(/\[sound:[^\]]+\]/, "").trim()
}
// 3. APPLY PAD: Inject un-strippable non-breaking space AFTER all regex text tags are removed
msg = "\u00A0" + msg
// 4. API INITIALIZATION
def url = "http://${settings.deviceIp}:8080/api/v2/device/notifications"
def authString = "dev:${settings.apiKey}"
def encodedAuth = "Basic " + authString.getBytes("UTF-8").encodeBase64().toString()
// 5. MAP DATA STRUCT
def modelData = [
frames: [
[
icon: activeIcon,
text: msg.replace('"', '\\"')
]
],
cycles: settings.cycles.toInteger()
]
// 6. EVALUATE SOUND ATTRIBUTES (Built-In Engine vs MP3 Stream)
if (activeSound != "none") {
if (activeSound.startsWith("http://") || activeSound.startsWith("https://")) {
modelData["sound"] = [
url: activeSound,
type: "mp3",
fallback: [
category: "notifications",
id: "notification"
]
]
} else {
def activeCategory = activeSound.startsWith("alarm") ? "alarms" : "notifications"
modelData["sound"] = [
category: activeCategory,
id: activeSound,
repeat: 1
]
}
}
def params = [
uri: url,
requestContentType: "application/json",
contentType: "application/json",
headers: [
"Authorization": encodedAuth
],
body: [ priority: "info", model: modelData ]
]
log.info "Transmitting LaMetric Context Payload: (Icon: ${activeIcon}, Audio target: ${activeSound}) -> ${msg}"
asynchttpPost('notificationResponse', params)
}
def notificationResponse(response, data) {
if (response.status == 200 || response.status == 201) {
log.debug "LaMetric transaction complete."
} else {
def errorDetail = "No extra error data returned."
try { if (response.hasError()) { errorDetail = response.getErrorData() } } catch (e) { }
log.error "LaMetric transaction failed. Status: ${response.status}. Reason: ${errorDetail}"
}
}
Use code with caution. It’s Beta and AI helped, so you know…
Initial Setup
-
Head to your Hubitat UI, navigate to Devices, click Add Device, and choose Virtual Device.
-
Set the Device Type dropdown to LaMetric Local Notifier and give it a label (like Living Room Clock).
-
Open your new device details preferences page, fill in your IP and API Key preferences, set a fallback default icon (e.g., static
i234or animateda234), select your baseline sound profile, and click Save Preferences. The lametric icon library can be found here
LaMetric: Web . -
Use the Commands tab to send a test notification.
Notification Syntax Examples
You can pass tokens inside you notification text to dynamically set the icon and / or play a sound:
-
Standard Message:
Mail has arrived!(Uses your default UI settings) -
Custom Static Icon Override:
[i1245] Front Door Motion Detected -
Custom Moving Animation Override:
[a5266] Security system armed! -
Sound Effect Override:
[sound:thunder] Severe weather warning issued! -
Full Custom Combo Overriding Everything:
[a4319] [sound:cash] Household electrical bill paid successfully -
Streaming an External Web MP3 Track:
[sound:https://google.com] Critical water leak detected!
the AI insisted on implementing this one - YMMV.
Native Sound Reference Guide
If you are using the dynamic rule format text parameters, use these literal System ID names inside your [sound:SystemID] strings:
Notification Tones
bicycle, car, cash, letter_email, open_door, statistic, thunder, water1, water2, wind, wind_short, cat, dog, dog2, energy, knock-knock, notification, notification2, notification3, notification4, win, win2, lose1, lose2
(Plus success sounds positive1 to positive6 and failure sounds negative1 to negative5)
Hardware Alarms
alarm1 through alarm13
Notes
I found this driver much easier to use with the NOAA Weather Alerts app. Here’s an example custom alert message for that app…
[a11428] [sound:thunder] NOAA {alertevent} {alerturgency} {alertheadline} {alertdescription} Area: {alertarea} Action: {alertinstruction}
Hope you guys find this useful.