Compare commits
6 Commits
feature/im
...
5ae34bf3d2
| Author | SHA1 | Date | |
|---|---|---|---|
|
5ae34bf3d2
|
|||
|
e1d1347b1e
|
|||
|
0436ff57d8
|
|||
|
8d9210247b
|
|||
|
1a5fe54213
|
|||
|
9f44554996
|
@@ -1,2 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
.env
|
.env
|
||||||
|
# Use the rust environment provided by the container
|
||||||
|
rust-toolchain.toml
|
||||||
|
|||||||
@@ -1,84 +1,24 @@
|
|||||||
# Based on: https://pastebin.com/99Fq2b2w
|
|
||||||
name: Build and deploy
|
name: Build and deploy
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- feature/**
|
- feature/**
|
||||||
|
tags:
|
||||||
|
- v*.*.*
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build application
|
uses: dreaded_x/workflows/.gitea/workflows/rust-kubernetes.yaml@22ee0c1788a8d2157db87d6a6f8dbe520fe48592
|
||||||
runs-on: ubuntu-latest
|
secrets: inherit
|
||||||
container: catthehacker/ubuntu:act-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Rust
|
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
||||||
with:
|
with:
|
||||||
rustflags: ""
|
upload_manifests: false
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: cargo build --release
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: automation
|
|
||||||
path: target/release/automation
|
|
||||||
|
|
||||||
container:
|
|
||||||
name: Build container
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build]
|
|
||||||
container: catthehacker/ubuntu:act-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Download artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: automation
|
|
||||||
|
|
||||||
- name: Set permissions
|
|
||||||
run: |
|
|
||||||
chown 65532:65532 ./automation
|
|
||||||
chmod 0755 ./automation
|
|
||||||
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: https://github.com/docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: git.huizinga.dev/dreaded_x/automation_rs
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=pr
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
|
|
||||||
- name: Login to registry
|
|
||||||
uses: https://github.com/docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: git.huizinga.dev
|
|
||||||
username: ${{ gitea.actor }}
|
|
||||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
uses: https://github.com/docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
name: Deploy container
|
name: Deploy container
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: catthehacker/ubuntu:act-latest
|
container: catthehacker/ubuntu:act-latest
|
||||||
needs: [container]
|
needs: build
|
||||||
if: gitea.ref == 'refs/heads/master'
|
if: gitea.ref == 'refs/heads/master'
|
||||||
steps:
|
steps:
|
||||||
- name: Stop and remove current container
|
- name: Stop and remove current container
|
||||||
@@ -97,11 +37,9 @@ jobs:
|
|||||||
-e MQTT_PASSWORD=${{ secrets.MQTT_PASSWORD }} \
|
-e MQTT_PASSWORD=${{ secrets.MQTT_PASSWORD }} \
|
||||||
-e HUE_TOKEN=${{ secrets.HUE_TOKEN }} \
|
-e HUE_TOKEN=${{ secrets.HUE_TOKEN }} \
|
||||||
-e NTFY_TOPIC=${{ secrets.NTFY_TOPIC }} \
|
-e NTFY_TOPIC=${{ secrets.NTFY_TOPIC }} \
|
||||||
git.huizinga.dev/dreaded_x/automation_rs:master
|
git.huizinga.dev/dreaded_x/automation_rs@${{ needs.build.outputs.digest }}
|
||||||
|
|
||||||
docker network connect web automation_rs
|
docker network connect web automation_rs
|
||||||
|
|
||||||
- name: Start container
|
- name: Start container
|
||||||
run: docker start automation_rs
|
run: docker start automation_rs
|
||||||
|
|
||||||
# TODO: Perform a healthcheck
|
|
||||||
|
|||||||
@@ -5,16 +5,12 @@ repos:
|
|||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
|
args:
|
||||||
|
- --allow-multiple-documents
|
||||||
- id: check-toml
|
- id: check-toml
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
- id: check-merge-conflict
|
- id: check-merge-conflict
|
||||||
|
|
||||||
- repo: https://github.com/doublify/pre-commit-rust
|
|
||||||
rev: v1.0
|
|
||||||
hooks:
|
|
||||||
- id: clippy
|
|
||||||
- id: fmt
|
|
||||||
|
|
||||||
- repo: https://github.com/JohnnyMorganz/StyLua
|
- repo: https://github.com/JohnnyMorganz/StyLua
|
||||||
rev: v0.20.0
|
rev: v0.20.0
|
||||||
hooks:
|
hooks:
|
||||||
@@ -26,7 +22,59 @@ repos:
|
|||||||
- id: typos
|
- id: typos
|
||||||
args: ["--force-exclude"]
|
args: ["--force-exclude"]
|
||||||
|
|
||||||
- repo: https://github.com/pryorda/dockerfilelint-precommit-hooks
|
- repo: local
|
||||||
rev: v0.1.0
|
|
||||||
hooks:
|
hooks:
|
||||||
- id: dockerfilelint
|
- id: fmt
|
||||||
|
name: fmt
|
||||||
|
description: Format files with cargo fmt.
|
||||||
|
entry: cargo +nightly fmt
|
||||||
|
language: system
|
||||||
|
types: [rust]
|
||||||
|
args: ["--", "--check"]
|
||||||
|
# For some reason some formatting is different depending on how you invoke?
|
||||||
|
pass_filenames: false
|
||||||
|
|
||||||
|
- id: clippy
|
||||||
|
name: clippy
|
||||||
|
description: Lint rust sources
|
||||||
|
entry: cargo clippy
|
||||||
|
language: system
|
||||||
|
args: ["--", "-D", "warnings"]
|
||||||
|
types: [file]
|
||||||
|
files: (\.rs|Cargo.lock)$
|
||||||
|
pass_filenames: false
|
||||||
|
|
||||||
|
- id: audit
|
||||||
|
name: audit
|
||||||
|
description: Audit packages
|
||||||
|
entry: cargo audit
|
||||||
|
args: ["--deny", "warnings"]
|
||||||
|
language: system
|
||||||
|
pass_filenames: false
|
||||||
|
verbose: true
|
||||||
|
always_run: true
|
||||||
|
|
||||||
|
- id: udeps
|
||||||
|
name: unused
|
||||||
|
description: Check for unused crates
|
||||||
|
entry: cargo udeps
|
||||||
|
args: ["--workspace"]
|
||||||
|
language: system
|
||||||
|
types: [file]
|
||||||
|
files: (\.rs|Cargo.lock)$
|
||||||
|
pass_filenames: false
|
||||||
|
|
||||||
|
- id: test
|
||||||
|
name: test
|
||||||
|
description: Rust test
|
||||||
|
entry: cargo test
|
||||||
|
language: system
|
||||||
|
args: ["--workspace"]
|
||||||
|
types: [file]
|
||||||
|
files: (\.rs|Cargo.lock)$
|
||||||
|
pass_filenames: false
|
||||||
|
|
||||||
|
- repo: https://github.com/hadolint/hadolint
|
||||||
|
rev: v2.12.0
|
||||||
|
hooks:
|
||||||
|
- id: hadolint
|
||||||
|
|||||||
1393
Cargo.lock
generated
1393
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "automation"
|
name = "automation"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
@@ -43,11 +43,9 @@ eui48 = { version = "1.1.0", features = [
|
|||||||
], default-features = false }
|
], default-features = false }
|
||||||
futures = "0.3.25"
|
futures = "0.3.25"
|
||||||
hostname = "0.4.0"
|
hostname = "0.4.0"
|
||||||
impls = "1.0.3"
|
|
||||||
indexmap = { version = "2.0.0", features = ["serde"] }
|
indexmap = { version = "2.0.0", features = ["serde"] }
|
||||||
itertools = "0.13.0"
|
itertools = "0.13.0"
|
||||||
json_value_merge = "2.0.0"
|
json_value_merge = "2.0.0"
|
||||||
pollster = "0.4.0"
|
|
||||||
proc-macro2 = "1.0.81"
|
proc-macro2 = "1.0.81"
|
||||||
quote = "1.0.36"
|
quote = "1.0.36"
|
||||||
reqwest = { version = "0.12.9", features = [
|
reqwest = { version = "0.12.9", features = [
|
||||||
|
|||||||
28
Dockerfile
28
Dockerfile
@@ -1,8 +1,26 @@
|
|||||||
FROM gcr.io/distroless/cc-debian12:nonroot
|
FROM rust:1.89 AS base
|
||||||
|
ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
|
||||||
|
RUN cargo install cargo-chef --locked --version 0.1.71 && \
|
||||||
|
cargo install cargo-auditable --locked --version 0.6.6
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
FROM base AS planner
|
||||||
|
COPY . .
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
|
FROM base AS builder
|
||||||
|
# HACK: Now we can use unstable feature while on stable rust!
|
||||||
|
ENV RUSTC_BOOTSTRAP=1
|
||||||
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
|
RUN cargo chef cook --release --recipe-path recipe.json
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
ARG RELEASE_VERSION
|
||||||
|
ENV RELEASE_VERSION=${RELEASE_VERSION}
|
||||||
|
RUN cargo auditable build --release
|
||||||
|
|
||||||
|
FROM gcr.io/distroless/cc-debian12:nonroot AS runtime
|
||||||
|
COPY --from=builder /app/target/release/automation /app/automation
|
||||||
ENV AUTOMATION_CONFIG=/app/config.lua
|
ENV AUTOMATION_CONFIG=/app/config.lua
|
||||||
COPY ./config.lua /app/config.lua
|
COPY ./config.lua /app/config.lua
|
||||||
|
CMD [ "/app/automation" ]
|
||||||
COPY ./automation /app/automation
|
|
||||||
|
|
||||||
CMD ["/app/automation"]
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "automation_cast"
|
name = "automation_cast"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "automation_devices"
|
name = "automation_devices"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
automation_lib = { workspace = true }
|
automation_lib = { workspace = true }
|
||||||
automation_macro = { workspace = true }
|
automation_macro = { workspace = true }
|
||||||
automation_cast = { workspace = true }
|
|
||||||
google_home = { workspace = true }
|
google_home = { workspace = true }
|
||||||
mlua = { workspace = true }
|
mlua = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
@@ -15,7 +14,6 @@ rumqttc = { workspace = true }
|
|||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
impls = { workspace = true }
|
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
reqwest = { workspace = true } # Use rustls, since the other packages also use rustls
|
reqwest = { workspace = true } # Use rustls, since the other packages also use rustls
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_lib::config::InfoConfig;
|
use automation_lib::config::InfoConfig;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use google_home::device::Name;
|
use google_home::device::Name;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
use google_home::traits::{
|
use google_home::traits::{
|
||||||
@@ -19,7 +19,8 @@ pub struct Config {
|
|||||||
pub url: String,
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
|
#[traits(OnOff)]
|
||||||
pub struct AirFilter {
|
pub struct AirFilter {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use automation_lib::event::{OnMqtt, OnPresence};
|
|||||||
use automation_lib::messages::{ContactMessage, PresenceMessage};
|
use automation_lib::messages::{ContactMessage, PresenceMessage};
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_lib::presence::DEFAULT_PRESENCE;
|
use automation_lib::presence::DEFAULT_PRESENCE;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use google_home::device;
|
use google_home::device;
|
||||||
use google_home::errors::{DeviceError, ErrorCode};
|
use google_home::errors::{DeviceError, ErrorCode};
|
||||||
use google_home::traits::OpenClose;
|
use google_home::traits::OpenClose;
|
||||||
@@ -61,18 +61,18 @@ struct State {
|
|||||||
handle: Option<JoinHandle<()>>,
|
handle: Option<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct ContactSensor {
|
pub struct ContactSensor {
|
||||||
config: Config,
|
config: Config,
|
||||||
state: Arc<RwLock<State>>,
|
state: Arc<RwLock<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContactSensor {
|
impl ContactSensor {
|
||||||
async fn state(&self) -> RwLockReadGuard<State> {
|
async fn state(&self) -> RwLockReadGuard<'_, State> {
|
||||||
self.state.read().await
|
self.state.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn state_mut(&self) -> RwLockWriteGuard<State> {
|
async fn state_mut(&self) -> RwLockWriteGuard<'_, State> {
|
||||||
self.state.write().await
|
self.state.write().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use automation_lib::device::{Device, LuaDeviceCreate};
|
|||||||
use automation_lib::event::{OnDarkness, OnPresence};
|
use automation_lib::event::{OnDarkness, OnPresence};
|
||||||
use automation_lib::messages::{DarknessMessage, PresenceMessage};
|
use automation_lib::messages::{DarknessMessage, PresenceMessage};
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use tracing::{trace, warn};
|
use tracing::{trace, warn};
|
||||||
|
|
||||||
#[derive(Debug, LuaDeviceConfig, Clone)]
|
#[derive(Debug, LuaDeviceConfig, Clone)]
|
||||||
@@ -18,7 +18,7 @@ pub struct Config {
|
|||||||
pub client: WrappedAsyncClient,
|
pub client: WrappedAsyncClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct DebugBridge {
|
pub struct DebugBridge {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::net::SocketAddr;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use automation_lib::event::{OnDarkness, OnPresence};
|
use automation_lib::event::{OnDarkness, OnPresence};
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ pub struct Config {
|
|||||||
pub flags: FlagIDs,
|
pub flags: FlagIDs,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct HueBridge {
|
pub struct HueBridge {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::net::SocketAddr;
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits::OnOff;
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
@@ -19,7 +19,8 @@ pub struct Config {
|
|||||||
pub scene_id: String,
|
pub scene_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
|
#[traits(OnOff)]
|
||||||
pub struct HueGroup {
|
pub struct HueGroup {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use automation_lib::config::{InfoConfig, MqttDeviceConfig};
|
|||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use automation_lib::event::OnMqtt;
|
use automation_lib::event::OnMqtt;
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use rumqttc::{matches, Publish};
|
use rumqttc::{Publish, matches};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ struct State {
|
|||||||
action: Action,
|
action: Action,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct HueSwitch {
|
pub struct HueSwitch {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use automation_lib::device::{Device, LuaDeviceCreate};
|
|||||||
use automation_lib::event::OnMqtt;
|
use automation_lib::event::OnMqtt;
|
||||||
use automation_lib::messages::{RemoteAction, RemoteMessage};
|
use automation_lib::messages::{RemoteAction, RemoteMessage};
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use axum::async_trait;
|
use axum::async_trait;
|
||||||
use rumqttc::{matches, Publish};
|
use rumqttc::{Publish, matches};
|
||||||
use tracing::{debug, error, trace};
|
use tracing::{debug, error, trace};
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
@@ -27,7 +27,7 @@ pub struct Config {
|
|||||||
pub callback: ActionCallback<IkeaRemote, bool>,
|
pub callback: ActionCallback<IkeaRemote, bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct IkeaRemote {
|
pub struct IkeaRemote {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::str::Utf8Error;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use automation_lib::event::OnPresence;
|
use automation_lib::event::OnPresence;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use bytes::{Buf, BufMut};
|
use bytes::{Buf, BufMut};
|
||||||
use google_home::errors::{self, DeviceError};
|
use google_home::errors::{self, DeviceError};
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits::OnOff;
|
||||||
@@ -22,7 +22,8 @@ pub struct Config {
|
|||||||
pub addr: SocketAddr,
|
pub addr: SocketAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
|
#[traits(OnOff)]
|
||||||
pub struct KasaOutlet {
|
pub struct KasaOutlet {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ mod wake_on_lan;
|
|||||||
mod washer;
|
mod washer;
|
||||||
mod zigbee;
|
mod zigbee;
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use automation_cast::Cast;
|
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use zigbee::light::{LightBrightness, LightColorTemperature, LightOnOff};
|
use zigbee::light::{LightBrightness, LightColorTemperature, LightOnOff};
|
||||||
use zigbee::outlet::{OutletOnOff, OutletPower};
|
use zigbee::outlet::{OutletOnOff, OutletPower};
|
||||||
@@ -37,128 +34,6 @@ macro_rules! register_device {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_device {
|
|
||||||
($device:ty) => {
|
|
||||||
impl mlua::UserData for $device {
|
|
||||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
|
||||||
methods.add_async_function("new", |_lua, config| async {
|
|
||||||
let device: $device = LuaDeviceCreate::create(config)
|
|
||||||
.await
|
|
||||||
.map_err(mlua::ExternalError::into_lua_err)?;
|
|
||||||
|
|
||||||
Ok(device)
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_method("__box", |_lua, this, _: ()| {
|
|
||||||
let b: Box<dyn Device> = Box::new(this.clone());
|
|
||||||
Ok(b)
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("get_id", |_lua, this, _: ()| async move { Ok(this.get_id()) });
|
|
||||||
|
|
||||||
if impls::impls!($device: google_home::traits::OnOff) {
|
|
||||||
methods.add_async_method("set_on", |_lua, this, on: bool| async move {
|
|
||||||
(this.deref().cast() as Option<&dyn google_home::traits::OnOff>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.set_on(on)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("on", |_lua, this, _: ()| async move {
|
|
||||||
Ok((this.deref().cast() as Option<&dyn google_home::traits::OnOff>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.on()
|
|
||||||
.await
|
|
||||||
.unwrap())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if impls::impls!($device: google_home::traits::Brightness) {
|
|
||||||
methods.add_async_method("set_brightness", |_lua, this, brightness: u8| async move {
|
|
||||||
(this.deref().cast() as Option<&dyn google_home::traits::Brightness>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.set_brightness(brightness)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("brightness", |_lua, this, _: ()| async move {
|
|
||||||
Ok((this.deref().cast() as Option<&dyn google_home::traits::Brightness>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.brightness()
|
|
||||||
.await
|
|
||||||
.unwrap())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if impls::impls!($device: google_home::traits::ColorSetting) {
|
|
||||||
methods.add_async_method("set_color_temperature", |_lua, this, temperature: u32| async move {
|
|
||||||
(this.deref().cast() as Option<&dyn google_home::traits::ColorSetting>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.set_color(google_home::traits::Color {temperature})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("color_temperature", |_lua, this, _: ()| async move {
|
|
||||||
Ok((this.deref().cast() as Option<&dyn google_home::traits::ColorSetting>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.color()
|
|
||||||
.await
|
|
||||||
.temperature)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if impls::impls!($device: google_home::traits::OpenClose) {
|
|
||||||
// TODO: Make discrete_only_open_close and query_only_open_close static, that way we can
|
|
||||||
// add only the supported functions and drop _percet if discrete is true
|
|
||||||
methods.add_async_method("set_open_percent", |_lua, this, open_percent: u8| async move {
|
|
||||||
(this.deref().cast() as Option<&dyn google_home::traits::OpenClose>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.set_open_percent(open_percent)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("open_percent", |_lua, this, _: ()| async move {
|
|
||||||
Ok((this.deref().cast() as Option<&dyn google_home::traits::OpenClose>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.open_percent()
|
|
||||||
.await
|
|
||||||
.unwrap())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_device!(LightOnOff);
|
|
||||||
impl_device!(LightBrightness);
|
|
||||||
impl_device!(LightColorTemperature);
|
|
||||||
impl_device!(OutletOnOff);
|
|
||||||
impl_device!(OutletPower);
|
|
||||||
impl_device!(AirFilter);
|
|
||||||
impl_device!(ContactSensor);
|
|
||||||
impl_device!(DebugBridge);
|
|
||||||
impl_device!(HueBridge);
|
|
||||||
impl_device!(HueGroup);
|
|
||||||
impl_device!(HueSwitch);
|
|
||||||
impl_device!(IkeaRemote);
|
|
||||||
impl_device!(KasaOutlet);
|
|
||||||
impl_device!(LightSensor);
|
|
||||||
impl_device!(WakeOnLAN);
|
|
||||||
impl_device!(Washer);
|
|
||||||
|
|
||||||
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
||||||
register_device!(lua, LightOnOff);
|
register_device!(lua, LightOnOff);
|
||||||
register_device!(lua, LightBrightness);
|
register_device!(lua, LightBrightness);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use automation_lib::device::{Device, LuaDeviceCreate};
|
|||||||
use automation_lib::event::{self, Event, EventChannel, OnMqtt};
|
use automation_lib::event::{self, Event, EventChannel, OnMqtt};
|
||||||
use automation_lib::messages::BrightnessMessage;
|
use automation_lib::messages::BrightnessMessage;
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
@@ -31,18 +31,18 @@ pub struct State {
|
|||||||
is_dark: bool,
|
is_dark: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct LightSensor {
|
pub struct LightSensor {
|
||||||
config: Config,
|
config: Config,
|
||||||
state: Arc<RwLock<State>>,
|
state: Arc<RwLock<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LightSensor {
|
impl LightSensor {
|
||||||
async fn state(&self) -> RwLockReadGuard<State> {
|
async fn state(&self) -> RwLockReadGuard<'_, State> {
|
||||||
self.state.read().await
|
self.state.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn state_mut(&self) -> RwLockWriteGuard<State> {
|
async fn state_mut(&self) -> RwLockWriteGuard<'_, State> {
|
||||||
self.state.write().await
|
self.state.write().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,9 +99,7 @@ impl OnMqtt for LightSensor {
|
|||||||
let is_dark = self.state().await.is_dark;
|
let is_dark = self.state().await.is_dark;
|
||||||
trace!(
|
trace!(
|
||||||
"In between min ({}) and max ({}) value, keeping current state: {}",
|
"In between min ({}) and max ({}) value, keeping current state: {}",
|
||||||
self.config.min,
|
self.config.min, self.config.max, is_dark
|
||||||
self.config.max,
|
|
||||||
is_dark
|
|
||||||
);
|
);
|
||||||
is_dark
|
is_dark
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use automation_lib::device::{Device, LuaDeviceCreate};
|
|||||||
use automation_lib::event::OnMqtt;
|
use automation_lib::event::OnMqtt;
|
||||||
use automation_lib::messages::ActivateMessage;
|
use automation_lib::messages::ActivateMessage;
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use eui48::MacAddress;
|
use eui48::MacAddress;
|
||||||
use google_home::device;
|
use google_home::device;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
@@ -28,7 +28,7 @@ pub struct Config {
|
|||||||
pub client: WrappedAsyncClient,
|
pub client: WrappedAsyncClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct WakeOnLAN {
|
pub struct WakeOnLAN {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use automation_lib::event::{self, Event, EventChannel, OnMqtt};
|
|||||||
use automation_lib::messages::PowerMessage;
|
use automation_lib::messages::PowerMessage;
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_lib::ntfy::{Notification, Priority};
|
use automation_lib::ntfy::{Notification, Priority};
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
@@ -31,18 +31,18 @@ pub struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add google home integration
|
// TODO: Add google home integration
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct Washer {
|
pub struct Washer {
|
||||||
config: Config,
|
config: Config,
|
||||||
state: Arc<RwLock<State>>,
|
state: Arc<RwLock<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Washer {
|
impl Washer {
|
||||||
async fn state(&self) -> RwLockReadGuard<State> {
|
async fn state(&self) -> RwLockReadGuard<'_, State> {
|
||||||
self.state.read().await
|
self.state.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn state_mut(&self) -> RwLockWriteGuard<State> {
|
async fn state_mut(&self) -> RwLockWriteGuard<'_, State> {
|
||||||
self.state.write().await
|
self.state.write().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ use automation_lib::device::{Device, LuaDeviceCreate};
|
|||||||
use automation_lib::event::{OnMqtt, OnPresence};
|
use automation_lib::event::{OnMqtt, OnPresence};
|
||||||
use automation_lib::helpers::serialization::state_deserializer;
|
use automation_lib::helpers::serialization::state_deserializer;
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use google_home::device;
|
use google_home::device;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
use google_home::traits::{Brightness, Color, ColorSetting, ColorTemperatureRange, OnOff};
|
use google_home::traits::{Brightness, Color, ColorSetting, ColorTemperatureRange, OnOff};
|
||||||
use google_home::types::Type;
|
use google_home::types::Type;
|
||||||
use rumqttc::{matches, Publish};
|
use rumqttc::{Publish, matches};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
@@ -88,7 +88,10 @@ impl From<StateColorTemperature> for StateBrightness {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
|
#[traits(<StateOnOff>: OnOff)]
|
||||||
|
#[traits(<StateBrightness>: OnOff, Brightness)]
|
||||||
|
#[traits(<StateColorTemperature>: OnOff, Brightness, ColorSetting)]
|
||||||
pub struct Light<T: LightState> {
|
pub struct Light<T: LightState> {
|
||||||
config: Config<T>,
|
config: Config<T>,
|
||||||
|
|
||||||
@@ -100,11 +103,11 @@ pub type LightBrightness = Light<StateBrightness>;
|
|||||||
pub type LightColorTemperature = Light<StateColorTemperature>;
|
pub type LightColorTemperature = Light<StateColorTemperature>;
|
||||||
|
|
||||||
impl<T: LightState> Light<T> {
|
impl<T: LightState> Light<T> {
|
||||||
async fn state(&self) -> RwLockReadGuard<T> {
|
async fn state(&self) -> RwLockReadGuard<'_, T> {
|
||||||
self.state.read().await
|
self.state.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn state_mut(&self) -> RwLockWriteGuard<T> {
|
async fn state_mut(&self) -> RwLockWriteGuard<'_, T> {
|
||||||
self.state.write().await
|
self.state.write().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ use automation_lib::device::{Device, LuaDeviceCreate};
|
|||||||
use automation_lib::event::{OnMqtt, OnPresence};
|
use automation_lib::event::{OnMqtt, OnPresence};
|
||||||
use automation_lib::helpers::serialization::state_deserializer;
|
use automation_lib::helpers::serialization::state_deserializer;
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use google_home::device;
|
use google_home::device;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits::OnOff;
|
||||||
use google_home::types::Type;
|
use google_home::types::Type;
|
||||||
use rumqttc::{matches, Publish};
|
use rumqttc::{Publish, matches};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
@@ -84,7 +84,9 @@ impl From<StatePower> for StateOnOff {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
|
#[traits(<StateOnOff>: OnOff)]
|
||||||
|
#[traits(<StatePower>: OnOff)]
|
||||||
pub struct Outlet<T: OutletState> {
|
pub struct Outlet<T: OutletState> {
|
||||||
config: Config<T>,
|
config: Config<T>,
|
||||||
|
|
||||||
@@ -95,11 +97,11 @@ pub type OutletOnOff = Outlet<StateOnOff>;
|
|||||||
pub type OutletPower = Outlet<StatePower>;
|
pub type OutletPower = Outlet<StatePower>;
|
||||||
|
|
||||||
impl<T: OutletState> Outlet<T> {
|
impl<T: OutletState> Outlet<T> {
|
||||||
async fn state(&self) -> RwLockReadGuard<T> {
|
async fn state(&self) -> RwLockReadGuard<'_, T> {
|
||||||
self.state.read().await
|
self.state.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn state_mut(&self) -> RwLockWriteGuard<T> {
|
async fn state_mut(&self) -> RwLockWriteGuard<'_, T> {
|
||||||
self.state.write().await
|
self.state.write().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "automation_lib"
|
name = "automation_lib"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
automation_macro = { workspace = true }
|
automation_macro = { workspace = true }
|
||||||
@@ -15,14 +15,11 @@ reqwest = { workspace = true }
|
|||||||
serde_repr = { workspace = true }
|
serde_repr = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
bytes = { workspace = true }
|
bytes = { workspace = true }
|
||||||
pollster = { workspace = true }
|
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
tokio-cron-scheduler = { workspace = true }
|
tokio-cron-scheduler = { workspace = true }
|
||||||
mlua = { workspace = true }
|
mlua = { workspace = true }
|
||||||
tokio-util = { workspace = true }
|
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
dyn-clone = { workspace = true }
|
dyn-clone = { workspace = true }
|
||||||
impls = { workspace = true }
|
|
||||||
|
|||||||
@@ -7,51 +7,6 @@ use mlua::ObjectLike;
|
|||||||
|
|
||||||
use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence};
|
use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence};
|
||||||
|
|
||||||
// TODO: Make this a proper macro
|
|
||||||
macro_rules! impl_device {
|
|
||||||
($device:ty) => {
|
|
||||||
impl mlua::UserData for $device {
|
|
||||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
|
||||||
methods.add_async_function("new", |_lua, config| async {
|
|
||||||
let device: $device = LuaDeviceCreate::create(config)
|
|
||||||
.await
|
|
||||||
.map_err(mlua::ExternalError::into_lua_err)?;
|
|
||||||
|
|
||||||
Ok(device)
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_method("__box", |_lua, this, _: ()| {
|
|
||||||
let b: Box<dyn Device> = Box::new(this.clone());
|
|
||||||
Ok(b)
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("get_id", |_lua, this, _: ()| async move { Ok(this.get_id()) });
|
|
||||||
|
|
||||||
if impls::impls!($device: google_home::traits::OnOff) {
|
|
||||||
methods.add_async_method("set_on", |_lua, this, on: bool| async move {
|
|
||||||
(this.deref().cast() as Option<&dyn google_home::traits::OnOff>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.set_on(on)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
methods.add_async_method("is_on", |_lua, this, _: ()| async move {
|
|
||||||
Ok((this.deref().cast() as Option<&dyn google_home::traits::OnOff>)
|
|
||||||
.expect("Cast should be valid")
|
|
||||||
.on()
|
|
||||||
.await
|
|
||||||
.unwrap())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub(crate) use impl_device;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait LuaDeviceCreate {
|
pub trait LuaDeviceCreate {
|
||||||
type Config;
|
type Config;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use std::collections::HashMap;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use futures::future::join_all;
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
use futures::future::join_all;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||||
use tokio_cron_scheduler::{Job, JobScheduler};
|
use tokio_cron_scheduler::{Job, JobScheduler};
|
||||||
use tracing::{debug, instrument, trace};
|
use tracing::{debug, instrument, trace};
|
||||||
@@ -64,7 +64,7 @@ impl DeviceManager {
|
|||||||
self.devices.read().await.get(name).cloned()
|
self.devices.read().await.get(name).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn devices(&self) -> RwLockReadGuard<DeviceMap> {
|
pub async fn devices(&self) -> RwLockReadGuard<'_, DeviceMap> {
|
||||||
self.devices.read().await
|
self.devices.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ impl fmt::Display for MissingEnv {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Missing environment variable")?;
|
write!(f, "Missing environment variable")?;
|
||||||
if self.keys.is_empty() {
|
if self.keys.is_empty() {
|
||||||
unreachable!("This error should only be returned if there are actually missing environment variables");
|
unreachable!(
|
||||||
|
"This error should only be returned if there are actually missing environment variables"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if self.keys.len() == 1 {
|
if self.keys.len() == 1 {
|
||||||
write!(f, " '{}'", self.keys[0])?;
|
write!(f, " '{}'", self.keys[0])?;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(specialization)]
|
|
||||||
#![feature(let_chains)]
|
|
||||||
|
|
||||||
pub mod action_callback;
|
pub mod action_callback;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
@@ -9,6 +7,7 @@ pub mod device_manager;
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
|
pub mod lua;
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
pub mod mqtt;
|
pub mod mqtt;
|
||||||
pub mod ntfy;
|
pub mod ntfy;
|
||||||
|
|||||||
1
automation_lib/src/lua/mod.rs
Normal file
1
automation_lib/src/lua/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod traits;
|
||||||
83
automation_lib/src/lua/traits.rs
Normal file
83
automation_lib/src/lua/traits.rs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
// TODO: Enable and disable functions based on query_only and command_only
|
||||||
|
|
||||||
|
pub trait OnOff {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M)
|
||||||
|
where
|
||||||
|
Self: Sized + google_home::traits::OnOff + 'static,
|
||||||
|
{
|
||||||
|
methods.add_async_method("set_on", |_lua, this, on: bool| async move {
|
||||||
|
this.deref().set_on(on).await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("on", |_lua, this, ()| async move {
|
||||||
|
Ok(this.deref().on().await.unwrap())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> OnOff for T where T: google_home::traits::OnOff {}
|
||||||
|
|
||||||
|
pub trait Brightness {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M)
|
||||||
|
where
|
||||||
|
Self: Sized + google_home::traits::Brightness + 'static,
|
||||||
|
{
|
||||||
|
methods.add_async_method("set_brightness", |_lua, this, brightness: u8| async move {
|
||||||
|
this.set_brightness(brightness).await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("brightness", |_lua, this, _: ()| async move {
|
||||||
|
Ok(this.brightness().await.unwrap())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> Brightness for T where T: google_home::traits::Brightness {}
|
||||||
|
|
||||||
|
pub trait ColorSetting {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M)
|
||||||
|
where
|
||||||
|
Self: Sized + google_home::traits::ColorSetting + 'static,
|
||||||
|
{
|
||||||
|
methods.add_async_method(
|
||||||
|
"set_color_temperature",
|
||||||
|
|_lua, this, temperature: u32| async move {
|
||||||
|
this.set_color(google_home::traits::Color { temperature })
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
methods.add_async_method("color_temperature", |_lua, this, ()| async move {
|
||||||
|
Ok(this.color().await.temperature)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> ColorSetting for T where T: google_home::traits::ColorSetting {}
|
||||||
|
|
||||||
|
pub trait OpenClose {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M)
|
||||||
|
where
|
||||||
|
Self: Sized + google_home::traits::OpenClose + 'static,
|
||||||
|
{
|
||||||
|
methods.add_async_method(
|
||||||
|
"set_open_percent",
|
||||||
|
|_lua, this, open_percent: u8| async move {
|
||||||
|
this.set_open_percent(open_percent).await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
methods.add_async_method("open_percent", |_lua, this, _: ()| async move {
|
||||||
|
Ok(this.open_percent().await.unwrap())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> OpenClose for T where T: google_home::traits::OpenClose {}
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_cast::Cast;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use automation_macro::LuaDeviceConfig;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_repr::*;
|
use serde_repr::*;
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
use crate::device::{impl_device, Device, LuaDeviceCreate};
|
use crate::device::{Device, LuaDeviceCreate};
|
||||||
use crate::event::{self, Event, EventChannel, OnNotification, OnPresence};
|
use crate::event::{self, Event, EventChannel, OnNotification, OnPresence};
|
||||||
|
|
||||||
#[derive(Debug, Serialize_repr, Clone, Copy)]
|
#[derive(Debug, Serialize_repr, Clone, Copy)]
|
||||||
@@ -121,13 +119,11 @@ pub struct Config {
|
|||||||
pub tx: event::Sender,
|
pub tx: event::Sender,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct Ntfy {
|
pub struct Ntfy {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_device!(Ntfy);
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl LuaDeviceCreate for Ntfy {
|
impl LuaDeviceCreate for Ntfy {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_cast::Cast;
|
use automation_macro::{LuaDevice, LuaDeviceConfig};
|
||||||
use automation_macro::LuaDeviceConfig;
|
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::device::{impl_device, Device, LuaDeviceCreate};
|
use crate::device::{Device, LuaDeviceCreate};
|
||||||
use crate::event::{self, Event, EventChannel, OnMqtt};
|
use crate::event::{self, Event, EventChannel, OnMqtt};
|
||||||
use crate::messages::PresenceMessage;
|
use crate::messages::PresenceMessage;
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
@@ -33,24 +31,22 @@ pub struct State {
|
|||||||
current_overall_presence: bool,
|
current_overall_presence: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, LuaDevice)]
|
||||||
pub struct Presence {
|
pub struct Presence {
|
||||||
config: Config,
|
config: Config,
|
||||||
state: Arc<RwLock<State>>,
|
state: Arc<RwLock<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Presence {
|
impl Presence {
|
||||||
async fn state(&self) -> RwLockReadGuard<State> {
|
async fn state(&self) -> RwLockReadGuard<'_, State> {
|
||||||
self.state.read().await
|
self.state.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn state_mut(&self) -> RwLockWriteGuard<State> {
|
async fn state_mut(&self) -> RwLockWriteGuard<'_, State> {
|
||||||
self.state.write().await
|
self.state.write().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_device!(Presence);
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl LuaDeviceCreate for Presence {
|
impl LuaDeviceCreate for Presence {
|
||||||
type Config = Config;
|
type Config = Config;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "automation_macro"
|
name = "automation_macro"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|||||||
88
automation_macro/src/impl_device.rs
Normal file
88
automation_macro/src/impl_device.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{ToTokens, quote};
|
||||||
|
use syn::parse::Parse;
|
||||||
|
use syn::punctuated::Punctuated;
|
||||||
|
use syn::{AngleBracketedGenericArguments, Attribute, DeriveInput, Ident, Path, Token};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Impl {
|
||||||
|
generics: Option<AngleBracketedGenericArguments>,
|
||||||
|
traits: Vec<Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Impl {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let generics = if input.peek(Token![<]) {
|
||||||
|
let generics = input.parse()?;
|
||||||
|
input.parse::<Token![:]>()?;
|
||||||
|
|
||||||
|
Some(generics)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let traits: Punctuated<_, _> = input.parse_terminated(Path::parse, Token![,])?;
|
||||||
|
let traits = traits.into_iter().collect();
|
||||||
|
|
||||||
|
Ok(Impl { generics, traits })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Impl {
|
||||||
|
fn generate(&self, name: &Ident) -> TokenStream {
|
||||||
|
let generics = &self.generics;
|
||||||
|
|
||||||
|
// If an identifier is specified, assume it is placed in ::automation_lib::lua::traits,
|
||||||
|
// otherwise use the provided path
|
||||||
|
let traits = self.traits.iter().map(|t| {
|
||||||
|
if let Some(ident) = t.get_ident() {
|
||||||
|
quote! {::automation_lib::lua::traits::#ident }
|
||||||
|
} else {
|
||||||
|
t.to_token_stream()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl mlua::UserData for #name #generics {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_function("new", |_lua, config| async {
|
||||||
|
let device: Self = LuaDeviceCreate::create(config)
|
||||||
|
.await
|
||||||
|
.map_err(mlua::ExternalError::into_lua_err)?;
|
||||||
|
|
||||||
|
Ok(device)
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("__box", |_lua, this, _: ()| {
|
||||||
|
let b: Box<dyn Device> = Box::new(this.clone());
|
||||||
|
Ok(b)
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("get_id", |_lua, this, _: ()| async move { Ok(this.get_id()) });
|
||||||
|
|
||||||
|
#(
|
||||||
|
#traits::add_methods(methods);
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn impl_device_macro(ast: &DeriveInput) -> TokenStream {
|
||||||
|
let name = &ast.ident;
|
||||||
|
|
||||||
|
let impls: TokenStream = ast
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.filter(|attr| attr.path().is_ident("traits"))
|
||||||
|
.flat_map(Attribute::parse_args::<Impl>)
|
||||||
|
.map(|im| im.generate(name))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if impls.is_empty() {
|
||||||
|
Impl::default().generate(name)
|
||||||
|
} else {
|
||||||
|
impls
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
#![feature(let_chains)]
|
|
||||||
#![feature(iter_intersperse)]
|
#![feature(iter_intersperse)]
|
||||||
|
mod impl_device;
|
||||||
mod lua_device_config;
|
mod lua_device_config;
|
||||||
|
|
||||||
use lua_device_config::impl_lua_device_config_macro;
|
use lua_device_config::impl_lua_device_config_macro;
|
||||||
use syn::{parse_macro_input, DeriveInput};
|
use syn::{DeriveInput, parse_macro_input};
|
||||||
|
|
||||||
|
use crate::impl_device::impl_device_macro;
|
||||||
|
|
||||||
#[proc_macro_derive(LuaDeviceConfig, attributes(device_config))]
|
#[proc_macro_derive(LuaDeviceConfig, attributes(device_config))]
|
||||||
pub fn lua_device_config_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn lua_device_config_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
@@ -11,3 +13,10 @@ pub fn lua_device_config_derive(input: proc_macro::TokenStream) -> proc_macro::T
|
|||||||
|
|
||||||
impl_lua_device_config_macro(&ast).into()
|
impl_lua_device_config_macro(&ast).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(LuaDevice, attributes(traits))]
|
||||||
|
pub fn impl_device(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
impl_device_macro(&ast).into()
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use syn::punctuated::Punctuated;
|
|||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::token::Paren;
|
use syn::token::Paren;
|
||||||
use syn::{
|
use syn::{
|
||||||
parenthesized, Data, DataStruct, DeriveInput, Expr, Field, Fields, FieldsNamed, LitStr, Result,
|
Data, DataStruct, DeriveInput, Expr, Field, Fields, FieldsNamed, LitStr, Result, Token, Type,
|
||||||
Token, Type,
|
parenthesized,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod kw {
|
mod kw {
|
||||||
@@ -155,7 +155,7 @@ fn field_from_lua(field: &Field) -> TokenStream {
|
|||||||
[] => field.ident.clone().unwrap().to_string(),
|
[] => field.ident.clone().unwrap().to_string(),
|
||||||
[rename] => rename.to_owned(),
|
[rename] => rename.to_owned(),
|
||||||
_ => {
|
_ => {
|
||||||
return quote_spanned! {field.span() => compile_error!("Field contains duplicate 'rename'")}
|
return quote_spanned! {field.span() => compile_error!("Field contains duplicate 'rename'")};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ fn field_from_lua(field: &Field) -> TokenStream {
|
|||||||
[] => quote! {panic!(#missing)},
|
[] => quote! {panic!(#missing)},
|
||||||
[default] => default.to_owned(),
|
[default] => default.to_owned(),
|
||||||
_ => {
|
_ => {
|
||||||
return quote_spanned! {field.span() => compile_error!("Field contains duplicate 'default'")}
|
return quote_spanned! {field.span() => compile_error!("Field contains duplicate 'default'")};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ fn field_from_lua(field: &Field) -> TokenStream {
|
|||||||
[] => value,
|
[] => value,
|
||||||
[value] => value.to_owned(),
|
[value] => value.to_owned(),
|
||||||
_ => {
|
_ => {
|
||||||
return quote_spanned! {field.span() => compile_error!("Only one of either 'from' or 'with' is allowed")}
|
return quote_spanned! {field.span() => compile_error!("Only one of either 'from' or 'with' is allowed")};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "google_home"
|
name = "google_home"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ use std::collections::HashMap;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use automation_cast::Cast;
|
use automation_cast::Cast;
|
||||||
use futures::future::{join_all, OptionFuture};
|
use futures::future::{OptionFuture, join_all};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::Device;
|
||||||
use crate::errors::{DeviceError, ErrorCode};
|
use crate::errors::{DeviceError, ErrorCode};
|
||||||
use crate::request::{self, Intent, Request};
|
use crate::request::{self, Intent, Request};
|
||||||
use crate::response::{self, execute, query, sync, Response, ResponsePayload};
|
use crate::response::{self, Response, ResponsePayload, execute, query, sync};
|
||||||
use crate::Device;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GoogleHome {
|
pub struct GoogleHome {
|
||||||
@@ -64,7 +64,7 @@ impl GoogleHome {
|
|||||||
devices: &HashMap<String, Box<T>>,
|
devices: &HashMap<String, Box<T>>,
|
||||||
) -> sync::Payload {
|
) -> sync::Payload {
|
||||||
let mut resp_payload = sync::Payload::new(&self.user_id);
|
let mut resp_payload = sync::Payload::new(&self.user_id);
|
||||||
let f = devices.iter().map(|(_, device)| async move {
|
let f = devices.values().map(|device| async move {
|
||||||
if let Some(device) = device.as_ref().cast() {
|
if let Some(device) = device.as_ref().cast() {
|
||||||
Some(Device::sync(device).await)
|
Some(Device::sync(device).await)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(specialization)]
|
#![feature(specialization)]
|
||||||
#![feature(let_chains)]
|
|
||||||
pub mod device;
|
pub mod device;
|
||||||
mod fulfillment;
|
mod fulfillment;
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use automation_cast::Cast;
|
|||||||
use google_home_macro::traits;
|
use google_home_macro::traits;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::errors::ErrorCode;
|
|
||||||
use crate::Device;
|
use crate::Device;
|
||||||
|
use crate::errors::ErrorCode;
|
||||||
|
|
||||||
traits! {
|
traits! {
|
||||||
Device,
|
Device,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "google_home_macro"
|
name = "google_home_macro"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#![feature(let_chains)]
|
|
||||||
#![feature(iter_intersperse)]
|
#![feature(iter_intersperse)]
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
@@ -6,8 +5,8 @@ use syn::parse::Parse;
|
|||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::token::Brace;
|
use syn::token::Brace;
|
||||||
use syn::{
|
use syn::{
|
||||||
braced, parse_macro_input, GenericArgument, Ident, LitStr, Path, PathArguments, PathSegment,
|
GenericArgument, Ident, LitStr, Path, PathArguments, PathSegment, ReturnType, Signature, Token,
|
||||||
ReturnType, Signature, Token, Type, TypePath,
|
Type, TypePath, braced, parse_macro_input,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod kw {
|
mod kw {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2024-12-06"
|
channel = "nightly-2025-08-20"
|
||||||
components = ["rustfmt", "clippy", "rust-analyzer"]
|
components = ["rustfmt", "clippy", "rust-analyzer"]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ use std::result;
|
|||||||
|
|
||||||
use axum::async_trait;
|
use axum::async_trait;
|
||||||
use axum::extract::{FromRef, FromRequestParts};
|
use axum::extract::{FromRef, FromRequestParts};
|
||||||
|
use axum::http::StatusCode;
|
||||||
use axum::http::request::Parts;
|
use axum::http::request::Parts;
|
||||||
use axum::http::status::InvalidStatusCode;
|
use axum::http::status::InvalidStatusCode;
|
||||||
use axum::http::StatusCode;
|
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|||||||
Reference in New Issue
Block a user