internal data sources (clock, static text), connection indicator, rendering fixes
DataSourceConfig refactored to enum: External/Clock/StaticText. Clock generates formatted time via chrono, static text emits configured string. ESP32: connection status indicator (green/red dot bottom-right), per-widget clear before redraw, RenderEvent enum for local + server messages. Polling uses DataUpdate instead of ScreenUpdate to avoid wiping widget state. Empty mappings passthrough raw source data for internal sources.
This commit is contained in:
@@ -9,13 +9,24 @@ pub enum DataSourceType {
|
||||
Rss,
|
||||
HttpJson,
|
||||
Webhook,
|
||||
Clock,
|
||||
StaticText,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataSourceConfig {
|
||||
pub url: Option<String>,
|
||||
pub headers: Vec<(String, String)>,
|
||||
pub api_key: Option<String>,
|
||||
pub enum DataSourceConfig {
|
||||
External {
|
||||
url: Option<String>,
|
||||
headers: Vec<(String, String)>,
|
||||
api_key: Option<String>,
|
||||
},
|
||||
Clock {
|
||||
format: String,
|
||||
timezone: String,
|
||||
},
|
||||
StaticText {
|
||||
text: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -38,18 +49,28 @@ impl DataSource {
|
||||
pub fn validate(&self) -> Vec<DataSourceValidationError> {
|
||||
let mut errors = Vec::new();
|
||||
|
||||
let is_webhook = self.source_type == DataSourceType::Webhook;
|
||||
|
||||
if is_webhook {
|
||||
if !self.poll_interval.is_zero() {
|
||||
errors.push(DataSourceValidationError::PollIntervalNotAllowed);
|
||||
match self.source_type {
|
||||
DataSourceType::Webhook => {
|
||||
if !self.poll_interval.is_zero() {
|
||||
errors.push(DataSourceValidationError::PollIntervalNotAllowed);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.poll_interval.is_zero() {
|
||||
errors.push(DataSourceValidationError::PollIntervalRequired);
|
||||
DataSourceType::Clock | DataSourceType::StaticText => {
|
||||
// Internal sources: poll_interval optional, no url needed
|
||||
}
|
||||
if self.requires_url() && self.config.url.is_none() {
|
||||
errors.push(DataSourceValidationError::UrlRequired);
|
||||
_ => {
|
||||
if self.poll_interval.is_zero() {
|
||||
errors.push(DataSourceValidationError::PollIntervalRequired);
|
||||
}
|
||||
if self.requires_url() {
|
||||
let has_url = matches!(
|
||||
&self.config,
|
||||
DataSourceConfig::External { url: Some(_), .. }
|
||||
);
|
||||
if !has_url {
|
||||
errors.push(DataSourceValidationError::UrlRequired);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,15 @@ impl WidgetConfig {
|
||||
}
|
||||
|
||||
pub fn extract(&self, raw: &Value) -> WidgetState {
|
||||
let has_mappings = self.mappings.iter().any(|m| !m.source_path.is_empty());
|
||||
if !has_mappings {
|
||||
let data = match raw {
|
||||
Value::Object(map) => map.clone(),
|
||||
_ => BTreeMap::new(),
|
||||
};
|
||||
return WidgetState { data, error: None };
|
||||
}
|
||||
|
||||
let budget = self.max_data_size as usize;
|
||||
let mut used = 0usize;
|
||||
let mut data = BTreeMap::new();
|
||||
|
||||
Reference in New Issue
Block a user