Cleanup db stuff

This commit is contained in:
Tobias Reisinger 2021-12-21 12:17:01 +01:00
parent 53ead537ff
commit f3f3d36eed
4 changed files with 950 additions and 66 deletions

860
api.v1.yaml Normal file
View file

@ -0,0 +1,860 @@
openapi: 3.0.0
info:
contact:
name: Tobias Reisinger
url: 'https://serguzim.me'
title: Emgauwa API v1
version: 0.0.1
description: Server API to manage an Emgauwa system.
servers:
- url: 'http://emgauwa-test-raspi.fritz.box'
- url: 'http://localhost:5000'
tags:
- name: schedules
- name: relays
- name: controllers
- name: tags
- name: macros
- name: websocket
paths:
/api/v1/schedules:
get:
summary: get all schedules
description: Receive a list with all available schedules.
tags:
- schedules
responses:
'200':
description: OK
headers: {}
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/schedule'
operationId: get-api-v1-schedules
post:
summary: add new schedule
tags:
- schedules
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/schedule'
operationId: post-api-v1-schedules
description: Create a new schedule. A new unique id will be returned
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/schedule'
description: The "id" field will be set by the server.
parameters: []
'/api/v1/schedules/{schedule_id}':
parameters:
- schema:
type: string
format: uuid
name: schedule_id
in: path
required: true
description: ''
get:
summary: get single schedule
tags:
- schedules
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/schedule'
'404':
description: Not Found
content:
application/json:
schema:
type: object
properties: {}
operationId: get-schedules-schedule_id
description: Return a single schedule by id.
put:
summary: overwrite single schedule
tags:
- schedules
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/schedule'
'400':
description: Bad Request
content:
application/json:
schema:
type: object
properties: {}
'404':
description: Not Found
content:
application/json:
schema:
type: object
properties: {}
operationId: put-schedules-schedule_id
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
periods:
type: array
items:
$ref: '#/components/schemas/period'
tags:
type: array
items:
$ref: '#/components/schemas/tag'
description: ''
parameters: []
description: Overwrite the properties for a single schedule. Overwriting periods on "on" or "off" will fail.
delete:
summary: delete single schedule
tags:
- schedules
responses:
'200':
description: OK
'403':
description: Forbidden
'404':
description: Not Found
operationId: delete-schedules-schedule_id
description: Deletes a single schedule. Deleting "on" or "off" is forbidden (403).
'/api/v1/schedules/tag/{tag}':
parameters:
- schema:
type: string
name: tag
in: path
required: true
description: ''
get:
summary: get schedules by tag
tags:
- schedules
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/schedule'
operationId: get-schedules-tag-schedule_id
description: Receive a list of schedules which include the given tag.
/api/v1/relays:
get:
summary: get all relays
tags:
- relays
responses:
'200':
description: OK
headers: {}
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/relay'
operationId: get-relays
description: Return a list with all relays.
parameters: []
'/api/v1/relays/tag/{tag}':
parameters:
- schema:
type: string
name: tag
in: path
required: true
get:
summary: get relays by tag
tags:
- relays
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/relay'
operationId: get-relays-tag-tag
description: Return all relays with the given tag.
/api/v1/controllers:
get:
summary: get all controllers
tags:
- controllers
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/controller'
operationId: get-controllers
description: Return all controllers.
parameters: []
/api/v1/controllers/discover:
put:
summary: discover controllers
tags:
- controllers
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/controller'
operationId: put-controllers-discover
description: Start a discovery process to find controllers in the network. This operations needs multiple seconds to complete.
parameters: []
'/api/v1/controllers/{controller_id}':
parameters:
- schema:
type: string
name: controller_id
in: path
description: ''
required: true
get:
summary: get single controller
tags:
- controllers
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/controller'
'404':
description: Not Found
content:
application/json:
schema:
type: object
properties: {}
operationId: get-controllers-controller_id
requestBody:
content:
application/json:
schema:
type: object
properties: {}
description: ''
description: Return a single controller by id. When no controller with the id is found 404 will be returned.
put:
summary: overwrite single controller
tags:
- controllers
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/controller'
'400':
description: Bad Request
content:
application/json:
schema:
type: object
properties: {}
'404':
description: Not Found
content:
application/json:
schema:
type: object
properties: {}
operationId: put-controllers-controller_id
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
ip:
type: string
format: ipv4
description: Overwrite properties of a single controller.
delete:
summary: delete single controller
tags:
- controllers
responses:
'200':
description: OK
'404':
description: Not Found
operationId: delete-controllers-controller_id
description: Delete a single controller. To recover the controller you need to use the conbtrollers/discover feature.
'/api/v1/controllers/{controller_id}/relays':
parameters:
- schema:
type: string
name: controller_id
in: path
required: true
get:
summary: get all relays for single controller
tags:
- controllers
- relays
responses:
'200':
description: OK
headers: {}
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/relay'
'404':
description: Not Found
content:
application/json:
schema:
type: array
items: {}
operationId: get-controllers-controller_id-relays
description: Returns all relays for a single controller.
'/api/v1/controllers/{controller_id}/relays/{relay_num}':
parameters:
- schema:
type: string
name: controller_id
in: path
required: true
- schema:
type: integer
name: relay_num
in: path
required: true
get:
summary: get single relay for single controller
tags:
- controllers
- relays
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/relay'
'404':
description: Not Found
content:
application/json:
schema:
type: object
properties: {}
operationId: get-controllers-controller_id-relays-relay_num
description: 'Return a single relay by number for a controller by id. When the relay or controller is not found, 404 will be returned.'
put:
summary: overwrite single relay for single controller
tags:
- controllers
- relays
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/relay'
'400':
description: Bad Request
content:
application/json:
schema:
type: object
properties: {}
'404':
description: Not Found
content:
application/json:
schema:
type: object
properties: {}
operationId: put-controllers-controller_id-relays-relay_num
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
active_schedule:
type: object
properties:
id:
$ref: '#/components/schemas/schedule_id'
schedules:
type: array
maxItems: 7
minItems: 7
items:
type: object
properties:
id:
$ref: '#/components/schemas/schedule_id'
tags:
type: array
items:
$ref: '#/components/schemas/tag'
description: 'active schedule will overwrite schedules[weekday]'
/api/v1/tags:
get:
summary: get all tags
tags:
- tags
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/tag'
operationId: get-tags
description: Returns a list of tags.
parameters: []
post:
summary: add new tag
operationId: post-api-v1-tags
responses:
'201':
description: Created
'400':
description: Bad Request
requestBody:
content:
application/json:
schema:
type: object
properties:
tag:
$ref: '#/components/schemas/tag'
description: ''
tags:
- tags
description: Add a new tag. Will return 400 when the tag already exits.
'/api/v1/tags/{tag}':
parameters:
- schema:
type: string
name: tag
in: path
required: true
get:
summary: get relays and schedules for tag
tags:
- tags
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
relays:
type: array
items:
$ref: '#/components/schemas/relay'
schedules:
type: array
items:
$ref: '#/components/schemas/schedule'
'404':
description: Not Found
operationId: get-tags-tag
description: Return all models with the given tag (relays and schedules)
delete:
summary: delete tag
operationId: delete-tags-tag
responses:
'200':
description: OK
'404':
description: Not Found
description: delete tag from database and from affected relays and schedules
tags:
- tags
'/api/v1/controllers/{controller_id}/relays/{relay_num}/pulse':
parameters:
- schema:
type: string
name: controller_id
in: path
required: true
- schema:
type: string
name: relay_num
in: path
required: true
post:
summary: pulse relay on
responses:
'200':
description: OK
'404':
description: Not Found
operationId: post-controllers-controller_id-relays-relay_num-pulse
requestBody:
content:
application/json:
schema:
type: object
properties:
duration:
type: integer
description: ''
description: Turn a relay on for a short amount of time. The duration can be set in the body in seconds. When no duration is supplied the default for the relay will be used. The default is read from the controller's config.
tags:
- controllers
- relays
/api/v1/ws/relays:
get:
summary: get relay status updates
tags:
- websocket
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/relay'
operationId: get-ws-relays
description: |-
WEBSOCKET
This websocket will send all relays with the most recent status every 10 seconds.
parameters: []
/api/v1/macros:
get:
summary: get all macros
tags:
- macros
responses:
'200':
description: OK
operationId: get-api-v1-macros
description: Receive a list with all available macros.
post:
summary: add new macro
tags:
- macros
responses:
'201':
description: Created
operationId: post-api-v1-macros
description: Create a new macro. A new unique id will be returned
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
actions:
type: array
items:
type: object
properties:
weekday:
type: integer
minimum: 0
maximum: 6
relay:
type: object
properties:
number:
type: integer
controller_id:
$ref: '#/components/schemas/controller_id'
schedule:
type: object
properties:
id:
$ref: '#/components/schemas/schedule_id'
'/api/v1/macros/{macro_id}':
parameters:
- schema:
type: string
name: macro_id
in: path
required: true
get:
summary: get a single macro
tags:
- macros
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/macro'
operationId: get-api-v1-macros-macro_id
description: Return a single macro by id. When no macro with the id is found 404 will be returned.
put:
summary: overwrite a macro
tags:
- macros
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/macro'
operationId: put-api-v1-macros-macro_id
description: Overwrite properties of a single macro.
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
actions:
type: array
items:
type: object
properties:
weekday:
type: integer
minimum: 0
maximum: 6
schedule:
type: object
properties:
id:
$ref: '#/components/schemas/schedule_id'
relay:
type: object
properties:
number:
type: integer
controller_id:
$ref: '#/components/schemas/controller_id'
delete:
summary: delete a macro
tags:
- macros
responses:
'200':
description: OK
operationId: delete-api-v1-macros-macro_id
description: Delete a single macro.
'/api/v1/macros/{macro_id}/execute':
parameters:
- schema:
type: string
name: macro_id
in: path
required: true
put:
summary: execute a macro
tags:
- macros
responses:
'200':
description: OK
'404':
description: Not Found
operationId: put-api-v1-macros-macro_id-execute
description: Execute a macro
/api/v1/schedules/list:
post:
summary: add new schedule list
tags:
- schedules
responses:
'200':
description: OK
operationId: post-schedules-list
requestBody:
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/schedule'
description: Create a list of schedules
parameters: []
components:
schemas:
controller:
title: controller
type: object
properties:
id:
$ref: '#/components/schemas/controller_id'
name:
type: string
example: Garden Controller
ip:
type: string
format: ipv4
example: 224.73.153.12
active:
type: boolean
port:
type: integer
example: 27480
relay_count:
type: integer
minimum: 0
example: 10
relays:
type: array
items:
$ref: '#/components/schemas/relay'
relay:
title: relay
type: object
properties:
number:
type: integer
minimum: 0
example: 3
name:
type: string
example: Sprinkling System 1
controller_id:
$ref: '#/components/schemas/controller_id'
active_schedule:
$ref: '#/components/schemas/schedule'
schedules:
type: array
maxItems: 7
minItems: 7
items:
$ref: '#/components/schemas/schedule'
tags:
type: array
items:
$ref: '#/components/schemas/tag'
is_on:
type: boolean
description: NULL when unknown
schedule:
title: schedule
type: object
description: ''
properties:
id:
$ref: '#/components/schemas/schedule_id'
name:
type: string
example: Sprinkler Sunny Day
periods:
type: array
items:
$ref: '#/components/schemas/period'
tags:
type: array
items:
$ref: '#/components/schemas/tag'
period:
title: period
type: object
properties:
start:
type: string
example: '10:15'
format: 24-hour
end:
type: string
format: 24-hour
example: '14:45'
required:
- start
- end
controller_id:
type: string
title: controller_id
format: uuid
example: 589c0eab-a4b4-4f3a-be97-cf03b1dc8edc
tag:
type: string
title: tag
example: sprinkler
schedule_id:
type: string
title: schedule_id
format: uuid
example: 6bceb29b-7d2e-4af3-a26e-11f514dc5cc1
macro:
title: macro
type: object
properties:
id:
type: string
format: uuid
example: a9a4eab4-6c54-4fe4-b755-bdb2a90b3242
name:
type: string
actions:
type: array
items:
$ref: '#/components/schemas/macro_action'
macro_action:
title: macro_action
type: object
description: ''
properties:
weekday:
type: integer
minimum: 0
maximum: 6
schedule:
$ref: '#/components/schemas/schedule'
relay:
$ref: '#/components/schemas/relay'
required:
- weekday
- schedule
- relay

View file

@ -13,6 +13,7 @@ use schema::schedules::dsl::*;
pub mod errors;
pub mod models;
pub mod schema;
mod model_utils;
embed_migrations!("migrations");

75
src/db/model_utils.rs Normal file
View file

@ -0,0 +1,75 @@
use chrono::{NaiveTime, Timelike};
use diesel::backend::Backend;
use diesel::deserialize::FromSql;
use diesel::serialize::{IsNull, Output, ToSql};
use diesel::sql_types::Binary;
use diesel::sqlite::Sqlite;
use diesel::{deserialize, serialize};
use serde::{Deserialize, Serialize};
use std::io::Write;
use crate::db::models::Periods;
#[derive(Debug, Serialize, Deserialize, AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
pub struct Period {
#[serde(with = "period_format")]
pub start: NaiveTime,
#[serde(with = "period_format")]
pub end: NaiveTime,
}
mod period_format {
use chrono::NaiveTime;
use serde::{self, Deserialize, Deserializer, Serializer};
const FORMAT: &'static str = "%H:%M";
pub fn serialize<S>(time: &NaiveTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = format!("{}", time.format(FORMAT));
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveTime, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
NaiveTime::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom)
}
}
impl ToSql<Binary, Sqlite> for Periods {
fn to_sql<W: Write>(&self, out: &mut Output<W, Sqlite>) -> serialize::Result {
for period in self.0.iter() {
out.write_all(&[
period.start.hour() as u8,
period.start.minute() as u8,
period.end.hour() as u8,
period.end.minute() as u8,
])?;
}
Ok(IsNull::No)
}
}
impl FromSql<Binary, Sqlite> for Periods {
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
let blob = bytes.unwrap().read_blob();
let mut vec = Vec::new();
for i in (3..blob.len()).step_by(4) {
let start_val_h: u32 = blob[i - 3] as u32;
let start_val_m: u32 = blob[i - 2] as u32;
let end_val_h: u32 = blob[i - 1] as u32;
let end_val_m: u32 = blob[i - 0] as u32;
vec.push(Period {
start: NaiveTime::from_hms(start_val_h, start_val_m, 0),
end: NaiveTime::from_hms(end_val_h, end_val_m, 0),
});
}
Ok(Periods(vec))
}
}

View file

@ -1,12 +1,6 @@
use chrono::{NaiveTime, Timelike};
use diesel::backend::Backend;
use diesel::deserialize::FromSql;
use diesel::serialize::{IsNull, Output, ToSql};
use diesel::sql_types::Binary;
use diesel::sqlite::Sqlite;
use diesel::{deserialize, serialize};
use serde::{Deserialize, Serialize};
use std::io::Write;
use crate::db::model_utils::Period;
use super::schema::schedules;
use crate::types::EmgauwaUid;
@ -29,71 +23,25 @@ pub struct NewSchedule<'a> {
pub periods: &'a Periods,
}
#[derive(Debug, Serialize, Deserialize, AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
pub struct Period {
#[serde(with = "period_format")]
pub start: NaiveTime,
#[serde(with = "period_format")]
pub end: NaiveTime,
}
#[derive(Debug, Serialize, Deserialize, AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
pub struct Periods(pub(crate) Vec<Period>);
mod period_format {
use chrono::NaiveTime;
use serde::{self, Deserialize, Deserializer, Serializer};
const FORMAT: &'static str = "%H:%M";
pub fn serialize<S>(time: &NaiveTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = format!("{}", time.format(FORMAT));
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<NaiveTime, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
NaiveTime::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom)
}
#[derive(Serialize, Queryable)]
pub struct Tag {
pub id: i32,
pub tag: String,
}
impl ToSql<Binary, Sqlite> for Periods {
fn to_sql<W: Write>(&self, out: &mut Output<W, Sqlite>) -> serialize::Result {
for period in self.0.iter() {
out.write_all(&[
period.start.hour() as u8,
period.start.minute() as u8,
period.end.hour() as u8,
period.end.minute() as u8,
])?;
}
Ok(IsNull::No)
}
#[derive(Insertable)]
#[table_name = "tags"]
pub struct NewTag<'a> {
pub tag: &'a str,
}
impl FromSql<Binary, Sqlite> for Periods {
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
let blob = bytes.unwrap().read_blob();
let mut vec = Vec::new();
for i in (3..blob.len()).step_by(4) {
let start_val_h: u32 = blob[i - 3] as u32;
let start_val_m: u32 = blob[i - 2] as u32;
let end_val_h: u32 = blob[i - 1] as u32;
let end_val_m: u32 = blob[i - 0] as u32;
vec.push(Period {
start: NaiveTime::from_hms(start_val_h, start_val_m, 0),
end: NaiveTime::from_hms(end_val_h, end_val_m, 0),
});
}
Ok(Periods(vec))
}
#[derive(Insertable)]
#[table_name = "junction_tag_schedule"]
pub struct NewJunctionTagSchedule<'a> {
pub tag_id: i32,
pub schedule_id: i32,
}