Files
smart-home-in-rust-with-hom…/home-assistant/src/notify/service/mobile_app/mod.rs

224 lines
6.9 KiB
Rust

use std::{convert::Infallible, str::FromStr};
use pyo3::{types::PyString, Bound, IntoPyObject, Python};
use python_utils::IntoPyObjectViaDisplay;
use snafu::Snafu;
use strum::EnumString;
use url::Url;
pub mod command;
pub mod standard;
pub mod text_to_speech;
pub use command::*;
pub use standard::StandardNotification;
pub use text_to_speech::TextToSpeech;
#[derive(Debug, Clone, derive_more::Display)]
pub struct NonSpecialMessage(String);
#[derive(Debug, Clone, EnumString, strum::Display)]
#[strum(serialize_all = "snake_case")]
pub enum SpecialMessage {
ClearBadge,
ClearNotification,
CommandActivity,
CommandAppLock,
CommandAutoScreenBrightness,
CommandBluetooth,
CommandBleTransmitter,
CommandBeaconMonitor,
CommandBroadcastIntent,
CommandDnd,
CommandFlashlight,
CommandHighAccuracyMode,
CommandLaunchApp,
CommandMedia,
CommandRingerMode,
CommandScreenBrightnessLevel,
CommandScreenOffTimeout,
CommandScreenOn,
CommandStopTts,
CommandPersistentConnection,
CommandUpdateSensors,
CommandVolumeLevel,
CommandWebivew,
RemoveChannel,
RequestLocationUpdate,
// #[strum(serialize = "TTS")] TODO: WIP: TESTING
Tts,
UpdateComplications,
UpdateWidgets,
}
/// wasn't supposed to get a specially-behaving message here, but got {got}
#[derive(Debug, Clone, Snafu)]
pub struct WasSpecialMessage {
pub got: SpecialMessage,
}
impl FromStr for NonSpecialMessage {
type Err = WasSpecialMessage;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match SpecialMessage::from_str(s) {
Ok(special_message) => Err(WasSpecialMessage {
got: special_message,
}),
Err(_e) => Ok(NonSpecialMessage(s.into())),
}
}
}
/// How much of a notification is visible on the lock screen
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
#[strum(serialize_all = "snake_case")]
pub enum Visibility {
/// always show all notification content
Public,
/// visibility depends on your setting in the system Settings app > Notifications;
/// if the option to show sensitive notifications when locked is enabled all notification content will be shown,
/// otherwise only basic information such as the icon and app name are visible
#[default]
Private,
/// always hide notification from lock screen
Secret,
}
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
impl<'py> IntoPyObject<'py> for Visibility {
type Target = PyString;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
let s = self.to_string();
s.into_pyobject(py)
}
}
#[derive(Debug, Clone, EnumString, strum::Display)]
pub enum Behavior {
/// prompt for text to return with the event
#[strum(serialize = "textInput")]
TextInput,
}
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
impl<'py> IntoPyObject<'py> for Behavior {
type Target = PyString;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
let s = self.to_string();
s.into_pyobject(py)
}
}
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
#[strum(serialize_all = "camelCase")]
pub enum ActivationMode {
/// launch the app when tapped
Foreground,
/// just fires the event
#[default]
Background,
}
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
impl<'py> IntoPyObject<'py> for ActivationMode {
type Target = PyString;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
let s = self.to_string();
s.into_pyobject(py)
}
}
// TODO: better typed versions like `CallNumber` or `OpenWebpage` where Action: From<CallNumber> and Action: From<OpenWebPage>
#[derive(Debug, Clone, IntoPyObject, typed_builder::TypedBuilder)]
#[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))]
pub struct Action {
// TODO: proper type
// TODO: I wish I could call this identifier or something instead
#[builder(!default, setter(!strip_option))]
pub action: String,
#[builder(!default, setter(!strip_option))]
pub title: String,
pub uri: Option<IntoPyObjectViaDisplay<Url>>,
pub behavior: Option<Behavior>,
// TODO: make this written as activationMode
/// (iOS only) decide whether to open the app (to the foreground) when tapped
/// or merely fire an event (in the background)
pub activation_mode: Option<ActivationMode>,
// TODO: make this written as authenticationRequired
/// (iOS only) require entering a passcode to use the action
pub authentication_required: Option<bool>,
/// (iOS only) color the action's title red, indicating a destructive action
pub destructive: Option<bool>,
// TODO: make this written as textInputButtonTitle
/// (iOS only) Title to use for text input for actions that prompt
pub text_input_button_title: Option<String>,
// TODO: make this written as textInputPlaceholder
/// (iOS only) Placeholder to use for text input for actions that prompt
pub text_input_placeholder: Option<String>,
// TODO: proper type
/// (iOS only) icon to use for the notification
pub icon: Option<String>,
}
#[derive(Debug, Clone, Default, EnumString, strum::Display)]
#[strum(serialize_all = "snake_case", suffix = "_stream")]
pub enum MediaStream {
Alarm,
Call,
Dtmf,
#[default]
Music,
Notification,
Ring,
System,
}
// TODO: replace with a derive(DisplayToPy) (analogous to serde_with::SerializeDisplay) once I make one
impl<'py> IntoPyObject<'py> for MediaStream {
type Target = PyString;
type Output = Bound<'py, Self::Target>;
type Error = Infallible;
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
let s = self.to_string();
s.into_pyobject(py)
}
}
#[derive(Debug, Default, Clone, IntoPyObject, typed_builder::TypedBuilder)]
#[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))]
pub struct NotifyMobileAppServiceDataData {
actions: Option<Vec<Action>>,
media_stream: Option<MediaStream>,
tts_text: Option<String>,
visibility: Option<Visibility>,
}
#[derive(Debug, Clone, IntoPyObject, typed_builder::TypedBuilder)]
#[builder(field_defaults(default, setter(strip_option(fallback_suffix = "_option"))))]
pub struct NotifyMobileAppServiceData {
#[builder(!default, setter(!strip_option))]
message: String,
title: Option<String>,
#[builder(setter(!strip_option))]
data: NotifyMobileAppServiceDataData,
}