websocket backend + frontend patching.
This commit is contained in:
Generated
+369
-312
File diff suppressed because it is too large
Load Diff
@@ -21,3 +21,4 @@ export default async function fetchLucifer<T>(path: string, init?: RequestInit):
|
||||
export async function fetchUIState(): Promise<UIState> {
|
||||
return fetchLucifer("state");
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import type Device from "$lib/models/device";
|
||||
import { SupportFlags } from "$lib/models/device";
|
||||
import { rgb, type ColorRGB } from "../models/color";
|
||||
import Icon from "./Icon.svelte";
|
||||
import DeviceIcon from "./DeviceIcon.svelte";
|
||||
|
||||
export let device: Device;
|
||||
|
||||
@@ -22,18 +22,18 @@
|
||||
let deviceTitle: string;
|
||||
let iconColor: ColorRGB | null;
|
||||
let barColor: ColorRGB | null;
|
||||
let barPercentage: number | null;
|
||||
let barFraction: number | null;
|
||||
let roundboiText: string | null;
|
||||
$: {
|
||||
// TODO: Fix device.name on the backend
|
||||
const nameAlias = device.aliases.find(a => a.startsWith("lucifer:name:"));
|
||||
const nameAlias = device.aliases?.find(a => a.startsWith("lucifer:name:"));
|
||||
if (nameAlias != null) {
|
||||
deviceTitle = nameAlias.slice("lucifer:name:".length);
|
||||
} else {
|
||||
deviceTitle = "";
|
||||
}
|
||||
|
||||
barPercentage = null;
|
||||
barFraction = null;
|
||||
barColor = null;
|
||||
iconColor = null;
|
||||
|
||||
@@ -46,29 +46,29 @@
|
||||
}
|
||||
|
||||
if (sflags & SupportFlags.Color) {
|
||||
iconColor = device.activeColorRgb;
|
||||
iconColor = device.desiredColorRgb;
|
||||
} else {
|
||||
iconColor = null;
|
||||
}
|
||||
|
||||
if (sflags & SupportFlags.SensorPresence) {
|
||||
barColor = rgb(0.5,0.5,0.5);
|
||||
barPercentage = Math.max(0, (300 - (device.sensors.lastMotion||300))/300) * 100;
|
||||
barFraction = Math.max(0, (300 - (device.sensors.lastMotion!=null?device.sensors.lastMotion:300))/300);
|
||||
}
|
||||
|
||||
if (sflags & SupportFlags.Temperature) {
|
||||
barColor = rgb(1.000,0.2,0.2);
|
||||
barPercentage = Math.min(1, Math.max(0, (device.desiredState.temperature||0) - 5) / 35) * 100;
|
||||
barFraction = Math.min(1, Math.max(0, (device.desiredState.temperature||0) - 5) / 35) * 1;
|
||||
}
|
||||
|
||||
if (sflags & SupportFlags.Intensity) {
|
||||
if (sflags & SupportFlags.Color) {
|
||||
barColor = device.activeColorRgb;
|
||||
barColor = device.desiredColorRgb;
|
||||
} else {
|
||||
barColor = rgb(1.000,0.671,0.355);
|
||||
}
|
||||
|
||||
barPercentage = device.desiredState.intensity;
|
||||
barFraction = device.desiredState.intensity;
|
||||
}
|
||||
|
||||
if (sflags & SupportFlags.SensorTemperature && !!device.sensors.temperature) {
|
||||
@@ -101,20 +101,20 @@
|
||||
<div class="row">
|
||||
<div class="row-icon">
|
||||
{#if iconColor != null}
|
||||
<Icon name={device.hwMetadata?.icon||"generic_ball"} brightColor={iconColor} darkColor={darkColor} />
|
||||
<DeviceIcon name={device.hwMetadata?.icon||"generic_ball"} brightColor={iconColor} darkColor={darkColor} />
|
||||
{:else}
|
||||
<Icon name={device.hwMetadata?.icon||"generic_ball"} brightColor={darkColor} darkColor={darkColor}>
|
||||
<DeviceIcon name={device.hwMetadata?.icon||"generic_ball"} brightColor={darkColor} darkColor={darkColor}>
|
||||
{#if !!roundboiText}
|
||||
<div class="roundboi-text">{roundboiText}</div>
|
||||
{/if}
|
||||
</Icon>
|
||||
</DeviceIcon>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="title">{displayTitle}</div>
|
||||
</div>
|
||||
<div class="flatboi">
|
||||
{#if barColor != null && barPercentage != null}
|
||||
<div style="width: {barPercentage*100}%; background-color: rgb({barColor.red*255},{barColor.green*255},{barColor.blue*255})" class="flatboi2"></div>
|
||||
{#if barColor != null && barFraction != null}
|
||||
<div style="width: {barFraction*100}%; background-color: rgb({barColor.red*255},{barColor.green*255},{barColor.blue*255})" class="flatboi2"></div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts" context="module">
|
||||
import { fetchUIState } from "$lib/client/lucifer";
|
||||
import type Device from "$lib/models/device";
|
||||
import type { UIStatePatch } from "$lib/models/uistate";
|
||||
import type UIState from "$lib/models/uistate";
|
||||
import { getContext, onMount, setContext } from "svelte";
|
||||
import { derived, writable, type Readable } from "svelte/store";
|
||||
@@ -9,7 +10,7 @@
|
||||
|
||||
export interface StateContextData {
|
||||
reload(): Promise<void>
|
||||
state: Readable<UIState | null>
|
||||
state: Readable<UIState>
|
||||
error: Readable<string | null>
|
||||
deviceList: Readable<Device[]>
|
||||
}
|
||||
@@ -20,7 +21,11 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
const state = writable<UIState | null>(null);
|
||||
const state = writable<UIState>({
|
||||
assignments: {},
|
||||
devices: {},
|
||||
script: {},
|
||||
});
|
||||
const error = writable<string | null>(null);
|
||||
|
||||
const deviceList = derived(state, state => {
|
||||
@@ -33,6 +38,8 @@
|
||||
.sort((a,b) => a.id.localeCompare(b.id));
|
||||
})
|
||||
|
||||
|
||||
|
||||
async function reload() {
|
||||
error.set(null);
|
||||
|
||||
@@ -44,7 +51,97 @@
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => { reload(); });
|
||||
async function connectSocket() {
|
||||
let url = `ws://${window.location.host}/subscribe`;
|
||||
if (import.meta.env.VITE_LUCIFER4_BACKEND_URL != null) {
|
||||
url = import.meta.env.VITE_LUCIFER4_BACKEND_URL.replace("http", "ws") +"/subscribe";
|
||||
}
|
||||
|
||||
const socket = new WebSocket(url);
|
||||
|
||||
socket.onmessage = (msg) => {
|
||||
const patch: UIStatePatch = JSON.parse(msg.data);
|
||||
|
||||
state.update(s => {
|
||||
if (patch.device) {
|
||||
if (patch.device.delete) {
|
||||
const devices = {...s.devices};
|
||||
delete devices[patch.device.id];
|
||||
return {...s, devices};
|
||||
} else {
|
||||
return {
|
||||
...s,
|
||||
devices: {
|
||||
...s.devices,
|
||||
[patch.device.id]: {
|
||||
...s.devices[patch.device.id],
|
||||
...patch.device,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (patch.assignment) {
|
||||
if (patch.assignment.delete) {
|
||||
const assignments = {...s.assignments};
|
||||
delete assignments[patch.assignment.id];
|
||||
return {...s, assignments};
|
||||
} else {
|
||||
return {
|
||||
...s,
|
||||
assignments: {
|
||||
...s.assignments,
|
||||
[patch.assignment.id]: {
|
||||
...s.assignments[patch.assignment.id],
|
||||
...patch.assignment,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (patch.script) {
|
||||
if (patch.script.delete) {
|
||||
const scripts = {...s.scripts};
|
||||
delete scripts[patch.script.id];
|
||||
return {...s, scripts};
|
||||
} else {
|
||||
return {
|
||||
...s,
|
||||
scripts: {
|
||||
...s.scripts,
|
||||
[patch.script.id]: {
|
||||
...s.scripts[patch.script.id],
|
||||
...patch.script,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
})
|
||||
}
|
||||
|
||||
socket.onerror = err => {
|
||||
console.warn("Socket failed:", err);
|
||||
socket.close();
|
||||
}
|
||||
|
||||
socket.onclose = () => {
|
||||
setTimeout(() => connectSocket(), 3000);
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
reload();
|
||||
const interval = setInterval(reload, 60000);
|
||||
|
||||
connectSocket();
|
||||
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
|
||||
setContext<StateContextData>(ctxKey, {
|
||||
reload,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { State } from "./device"
|
||||
|
||||
export default interface Assignment {
|
||||
id: string
|
||||
deviceIds: string[]
|
||||
effect: Effect
|
||||
variables: Record<string, number>
|
||||
}
|
||||
|
||||
export type Effect =
|
||||
| { manual: State }
|
||||
| { gradient: { states: State[], animationMs: number, reverse: boolean, interpolate: boolean } }
|
||||
| { pattern: { states: State[], animationMs: number } }
|
||||
| { random: { states: State[], animationMs: number, interpolate: boolean } }
|
||||
| { vrange: { states: State[], variable: string, min: number, max: number } }
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IconName } from "$lib/components/Icon.svelte"
|
||||
import type { IconName } from "$lib/components/DeviceIcon.svelte"
|
||||
import type { ColorFlags, ColorRGB } from "./color"
|
||||
|
||||
export default interface Device {
|
||||
@@ -6,6 +6,7 @@ export default interface Device {
|
||||
name: string
|
||||
hwMetadata: HardwareMetadata | null
|
||||
hwState: HardwareState | null
|
||||
desiredColorRgb: ColorRGB | null
|
||||
activeColorRgb: ColorRGB | null
|
||||
desiredState: State,
|
||||
aliases: string[],
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { Effect } from "./assignment"
|
||||
|
||||
export default interface Script {
|
||||
name: string
|
||||
lines: ScriptLine[]
|
||||
}
|
||||
|
||||
export interface ScriptLine {
|
||||
if: ScriptLineIf
|
||||
assign: ScriptLineAssign
|
||||
set: ScriptLineSet
|
||||
}
|
||||
|
||||
export interface ScriptLineIf {
|
||||
condition: ScriptCondition
|
||||
then: ScriptLine[]
|
||||
else: ScriptLine[]
|
||||
}
|
||||
|
||||
export interface ScriptCondition {
|
||||
scope: string
|
||||
key: string
|
||||
op: string
|
||||
value?: string
|
||||
not?: boolean
|
||||
}
|
||||
|
||||
export interface ScriptLineAssign {
|
||||
match: string
|
||||
effect: Effect
|
||||
}
|
||||
|
||||
export interface ScriptLineSet {
|
||||
scope: string
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
@@ -1,5 +1,15 @@
|
||||
import type Assignment from "./assignment";
|
||||
import type Device from "./device";
|
||||
import type Script from "./script";
|
||||
|
||||
export default interface UIState {
|
||||
devices: {[id: string]: Device}
|
||||
assignments: {[id: string]: Assignment}
|
||||
scripts: {[id: string]: Script}
|
||||
}
|
||||
|
||||
export interface UIStatePatch {
|
||||
device: Partial<Device> & { id: string, delete?: boolean }
|
||||
assignment: Partial<Assignment> & { id: string, delete?: boolean }
|
||||
script: Partial<Script> & { id: string, delete?: boolean }
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Icon from "$lib/components/Icon.svelte";
|
||||
import DeviceIcon from "$lib/components/DeviceIcon.svelte";
|
||||
import Lamp, { DARK_COLOR } from "$lib/components/Lamp.svelte";
|
||||
import Toolbar from "$lib/components/Toolbar.svelte";
|
||||
import { getStateContext } from "$lib/contexts/StateContext.svelte";
|
||||
@@ -14,18 +14,18 @@ import Lamp, { DARK_COLOR } from "$lib/components/Lamp.svelte";
|
||||
{/each}
|
||||
</div>
|
||||
<div class="page" style="font-size: 8em">
|
||||
<Icon name="generic_ball" brightColor={rgb(0.517,0.537,1.000)} darkColor={DARK_COLOR} />
|
||||
<Icon name="generic_boob" brightColor={rgb(0.517,0.537,1.000)} darkColor={DARK_COLOR} />
|
||||
<Icon name="generic_lamp" brightColor={rgb(0.667,0.750,1.000)} darkColor={DARK_COLOR} />
|
||||
<Icon name="generic_strip" brightColor={rgb(0.667,0.750,1.000)} darkColor={DARK_COLOR} />
|
||||
<Icon name="hue_adore_tube" brightColor={rgb(1,1,0)} darkColor={DARK_COLOR} />
|
||||
<Icon name="hue_signe" brightColor={rgb(0.300,1.000,0.300)} darkColor={DARK_COLOR} />
|
||||
<Icon name="hue_dimmerswitch" brightColor={rgb(0.517,0.537,1.000)} darkColor={DARK_COLOR} />
|
||||
<Icon name="hue_playbar" brightColor={rgb(1,0,1)} darkColor={DARK_COLOR} />
|
||||
<Icon name="hue_motionsensor" brightColor={rgb(1,1,1)} darkColor={DARK_COLOR} />
|
||||
<Icon name="hue_lightbulb_e27" brightColor={rgb(1,1,1)} darkColor={DARK_COLOR} />
|
||||
<Icon name="hue_lightbulb_e14" brightColor={rgb(1,1,1)} darkColor={DARK_COLOR} />
|
||||
<Icon name="hue_lightbulb_gu10" brightColor={rgb(1,1,1)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="generic_ball" brightColor={rgb(0.517,0.537,1.000)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="generic_boob" brightColor={rgb(0.517,0.537,1.000)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="generic_lamp" brightColor={rgb(0.667,0.750,1.000)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="generic_strip" brightColor={rgb(0.667,0.750,1.000)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="hue_adore_tube" brightColor={rgb(1,1,0)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="hue_signe" brightColor={rgb(0.300,1.000,0.300)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="hue_dimmerswitch" brightColor={rgb(0.517,0.537,1.000)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="hue_playbar" brightColor={rgb(1,0,1)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="hue_motionsensor" brightColor={rgb(1,1,1)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="hue_lightbulb_e27" brightColor={rgb(1,1,1)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="hue_lightbulb_e14" brightColor={rgb(1,1,1)} darkColor={DARK_COLOR} />
|
||||
<DeviceIcon name="hue_lightbulb_gu10" brightColor={rgb(1,1,1)} darkColor={DARK_COLOR} />
|
||||
</div>
|
||||
<Toolbar />
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ require (
|
||||
github.com/eriklupander/dtls v0.0.0-20190304211642-b36018226359 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/protobuf v1.3.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
|
||||
@@ -30,6 +30,8 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
|
||||
@@ -8,10 +8,12 @@ import (
|
||||
"git.aiterp.net/lucifer3/server/services/script"
|
||||
"git.aiterp.net/lucifer3/server/services/uistate"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -24,6 +26,11 @@ var zeroUUID = uuid.UUID{
|
||||
|
||||
func New(addr string) (lucifer3.Service, error) {
|
||||
svc := &service{}
|
||||
upgrader := websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
e := echo.New()
|
||||
e.HideBanner = true
|
||||
@@ -38,6 +45,28 @@ func New(addr string) (lucifer3.Service, error) {
|
||||
return c.JSON(200, data)
|
||||
})
|
||||
|
||||
e.GET("/subscribe", func(c echo.Context) error {
|
||||
ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sub := make(chan uistate.Patch, 64)
|
||||
svc.addSub(sub)
|
||||
|
||||
defer svc.removeSub(sub)
|
||||
defer ws.Close()
|
||||
|
||||
for patch := range sub {
|
||||
err := ws.WriteJSON(patch)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
e.POST("/command", func(c echo.Context) error {
|
||||
var input commandInput
|
||||
err := c.Bind(&input)
|
||||
@@ -115,6 +144,7 @@ type service struct {
|
||||
mu sync.Mutex
|
||||
data uistate.Data
|
||||
bus *lucifer3.EventBus
|
||||
subs []chan uistate.Patch
|
||||
}
|
||||
|
||||
func (s *service) Active() bool {
|
||||
@@ -130,12 +160,36 @@ func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
||||
case uistate.Patch:
|
||||
s.mu.Lock()
|
||||
s.data = s.data.WithPatch(event)
|
||||
subs := s.subs
|
||||
s.mu.Unlock()
|
||||
|
||||
// TODO: Broadcast websockets
|
||||
for _, sub := range subs {
|
||||
select {
|
||||
case sub <- event:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) addSub(ch chan uistate.Patch) {
|
||||
s.mu.Lock()
|
||||
s.subs = append(s.subs, ch)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *service) removeSub(ch chan uistate.Patch) {
|
||||
s.mu.Lock()
|
||||
s.subs = append([]chan uistate.Patch{}, s.subs...)
|
||||
for i, sub := range s.subs {
|
||||
if sub == ch {
|
||||
s.subs = append(s.subs[:i], s.subs[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
type commandInput struct {
|
||||
Assign *assignInput `json:"assign,omitempty"`
|
||||
AddAlias *commands.AddAlias `json:"addAlias,omitempty"`
|
||||
|
||||
@@ -155,7 +155,7 @@ func (b *Bridge) RefreshAll() ([]lucifer3.Event, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if res.Type == "device" {
|
||||
if res.Type == "device" && !strings.HasPrefix(res.Metadata.Archetype, "bridge") {
|
||||
hwState, hwEvent := res.GenerateEvent(b.host, resources)
|
||||
if hwState.SupportFlags == 0 {
|
||||
continue
|
||||
@@ -219,7 +219,7 @@ func (b *Bridge) ApplyPatches(date time.Time, resources []ResourceData) (eventLi
|
||||
|
||||
for _, resource := range resources {
|
||||
if resource.Owner != nil && resource.Owner.Kind == "device" {
|
||||
if parent, ok := mapCopy[resource.Owner.ID]; ok {
|
||||
if parent, ok := mapCopy[resource.Owner.ID]; ok && !strings.HasPrefix(parent.Metadata.Archetype, "bridge") {
|
||||
hwState, _ := parent.GenerateEvent(b.host, mapCopy)
|
||||
eventList = append(eventList, hwState)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ func (d *Data) WithPatch(patches ...Patch) Data {
|
||||
gentools.ApplyUpdatePtr(&pd.DesiredState, patch.Device.DesiredState)
|
||||
gentools.ApplyUpdatePtr(&pd.Assignment, patch.Device.Assignment)
|
||||
gentools.ApplyUpdatePtr(&pd.ActiveColorRGB, patch.Device.ActiveColorRGB)
|
||||
gentools.ApplyUpdatePtr(&pd.DesiredColorRGB, patch.Device.DesiredColorRGB)
|
||||
|
||||
if patch.Device.AddAlias != nil {
|
||||
pd.Aliases = append(pd.Aliases[:0:0], pd.Aliases...)
|
||||
@@ -125,15 +126,16 @@ func (d *Data) ensureAssignment(id uuid.UUID) Assignment {
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
HWMetadata *events.HardwareMetadata `json:"hwMetadata"`
|
||||
HWState *events.HardwareState `json:"hwState"`
|
||||
ActiveColorRGB *color.RGB `json:"activeColorRgb"`
|
||||
DesiredState *device.State `json:"desiredState"`
|
||||
Aliases []string `json:"aliases"`
|
||||
Assignment *uuid.UUID `json:"assignment"`
|
||||
Sensors DeviceSensors `json:"sensors"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
HWMetadata *events.HardwareMetadata `json:"hwMetadata"`
|
||||
HWState *events.HardwareState `json:"hwState"`
|
||||
DesiredColorRGB *color.RGB `json:"desiredColorRgb"`
|
||||
ActiveColorRGB *color.RGB `json:"activeColorRgb"`
|
||||
DesiredState *device.State `json:"desiredState"`
|
||||
Aliases []string `json:"aliases"`
|
||||
Assignment *uuid.UUID `json:"assignment"`
|
||||
Sensors DeviceSensors `json:"sensors"`
|
||||
}
|
||||
|
||||
type DeviceSensors struct {
|
||||
|
||||
@@ -38,6 +38,9 @@ func (e Patch) EventDescription() string {
|
||||
case e.Device.ActiveColorRGB != nil:
|
||||
col := color.Color{RGB: e.Device.ActiveColorRGB}
|
||||
return fmt.Sprintf("uistate.Patch(device=%s, activeColorRgb=%s)", e.Device.ID, col.String())
|
||||
case e.Device.DesiredColorRGB != nil:
|
||||
col := color.Color{RGB: e.Device.DesiredColorRGB}
|
||||
return fmt.Sprintf("uistate.Patch(device=%s, desiredColorRgb=%s)", e.Device.ID, col.String())
|
||||
default:
|
||||
return fmt.Sprintf("uistate.Patch(device=%s, ...other)", e.Device.ID)
|
||||
}
|
||||
@@ -61,6 +64,7 @@ type DevicePatch struct {
|
||||
RemoveAlias *string `json:"removeAlias,omitempty"`
|
||||
Assignment *uuid.UUID `json:"assignment,omitempty"`
|
||||
ClearAssignment bool `json:"clearAssignment,omitempty"`
|
||||
DesiredColorRGB *color.RGB `json:"desiredColorRgb,omitempty"`
|
||||
ActiveColorRGB *color.RGB `json:"activeColorRgb,omitempty"`
|
||||
ClearActiveColorRGB bool `json:"clearActiveColorRGB,omitempty"`
|
||||
Sensors *DeviceSensors `json:"sensors,omitempty"`
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"git.aiterp.net/lucifer3/server/commands"
|
||||
"git.aiterp.net/lucifer3/server/effects"
|
||||
"git.aiterp.net/lucifer3/server/events"
|
||||
"git.aiterp.net/lucifer3/server/internal/color"
|
||||
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||
"git.aiterp.net/lucifer3/server/services/script"
|
||||
"github.com/google/uuid"
|
||||
@@ -36,11 +37,29 @@ func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command
|
||||
|
||||
switch command := command.(type) {
|
||||
case commands.SetState:
|
||||
patches = []Patch{{Device: &DevicePatch{ID: command.ID, DesiredState: &command.State}}}
|
||||
rgb := color.Color{}
|
||||
if command.State.Color != nil {
|
||||
rgb, _ = command.State.Color.ToRGB()
|
||||
}
|
||||
|
||||
patches = []Patch{{Device: &DevicePatch{
|
||||
ID: command.ID,
|
||||
DesiredState: &command.State,
|
||||
DesiredColorRGB: rgb.RGB,
|
||||
}}}
|
||||
case commands.SetStateBatch:
|
||||
for id, state := range command {
|
||||
rgb := color.Color{}
|
||||
if state.Color != nil {
|
||||
rgb, _ = state.Color.ToRGB()
|
||||
}
|
||||
|
||||
patches = append(patches, Patch{
|
||||
Device: &DevicePatch{ID: id, DesiredState: gentools.ShallowCopy(&state)},
|
||||
Device: &DevicePatch{
|
||||
ID: id,
|
||||
DesiredState: gentools.ShallowCopy(&state),
|
||||
DesiredColorRGB: rgb.RGB,
|
||||
},
|
||||
})
|
||||
}
|
||||
case script.Update:
|
||||
@@ -129,10 +148,13 @@ func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
||||
}})
|
||||
case events.AssignmentVariables:
|
||||
// Always set the assignment
|
||||
patches = append(patches, Patch{Assignment: &AssignmentPatch{
|
||||
ID: event.ID,
|
||||
Variables: event.Map,
|
||||
}})
|
||||
if len(event.Map) > 0 {
|
||||
patches = append(patches, Patch{Assignment: &AssignmentPatch{
|
||||
ID: event.ID,
|
||||
Variables: event.Map,
|
||||
}})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(patches) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user