From a2e719def937aecfef728fe827c6d61b4a737aaa Mon Sep 17 00:00:00 2001 From: Stian Fredrik Aune Date: Mon, 24 May 2021 10:31:31 +0200 Subject: [PATCH] Bridge events --- app/services/bridges.go | 50 +++++++++++++++++++++++++++++ app/services/events.go | 32 ++++++++++++++++++ internal/drivers/nanoleaf/bridge.go | 4 +-- models/event.go | 16 +++++++-- models/shared.go | 6 ++-- webui/src/primitives/Forms.tsx | 4 +-- webui/src/setupProxy.js | 8 +++++ 7 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 app/services/bridges.go create mode 100644 webui/src/setupProxy.js diff --git a/app/services/bridges.go b/app/services/bridges.go new file mode 100644 index 0000000..816ffd1 --- /dev/null +++ b/app/services/bridges.go @@ -0,0 +1,50 @@ +package services + +import ( + "context" + "git.aiterp.net/lucifer/new-server/app/config" + "log" + "time" +) + +var cancelMap = make(map[int]context.CancelFunc, 8) + +func ConnectToBridges() { + go func() { + for { + err := runConnectToBridges() + if err != nil { + log.Println("Bridge connection error: ", err.Error()) + } + + time.Sleep(15 * time.Second) + } + }() +} + +func runConnectToBridges() error { + bridges, err := config.BridgeRepository().FetchAll(context.Background()) + if err != nil { + return err + } + + for _, bridge := range bridges { + if cancelMap[bridge.ID] != nil { + continue + } + + driver, err := config.DriverProvider().Provide(bridge.Driver) + if err != nil { + return err + } + + ctx, cancel := context.WithCancel(context.Background()) + err = driver.Run(ctx, bridge, config.EventChannel) + + cancelMap[bridge.ID] = cancel + + log.Printf("Connected to bridge \"%s\" (%d)", bridge.Name, bridge.ID) + } + + return nil +} diff --git a/app/services/events.go b/app/services/events.go index b296616..29b2698 100644 --- a/app/services/events.go +++ b/app/services/events.go @@ -5,6 +5,7 @@ import ( "git.aiterp.net/lucifer/new-server/app/config" "git.aiterp.net/lucifer/new-server/models" "log" + "strconv" "time" ) @@ -40,6 +41,12 @@ func handleEvent(event models.Event) { event.AddPayload("minute", time.Now().In(loc).Format("04")) } + err := handleSpecial(event) + if err != nil { + log.Printf("Special event handler error (%s): %v", event.Name, err) + return + } + if !X { log.Println("Unhandled event: " + event.Name) return @@ -64,3 +71,28 @@ func handleEvent(event models.Event) { } } + +func handleSpecial(event models.Event) error { + switch event.Name { + case models.ENBridgeConnected: + bridgeId, _ := strconv.Atoi(event.Payload["bridgeId"]) + bridge, err := config.BridgeRepository().Find(ctx, bridgeId) + if err != nil { + return err + } + + devices, err := config.DeviceRepository().FetchByReference(ctx, models.RKBridgeID, event.Payload["bridgeId"]) + if err != nil { + return err + } + + driver, err := config.DriverProvider().Provide(bridge.Driver) + if err != nil { + return err + } + + return driver.Publish(ctx, bridge, devices) + default: + return nil + } +} diff --git a/internal/drivers/nanoleaf/bridge.go b/internal/drivers/nanoleaf/bridge.go index 2962114..0b32ce6 100644 --- a/internal/drivers/nanoleaf/bridge.go +++ b/internal/drivers/nanoleaf/bridge.go @@ -195,9 +195,9 @@ func (b *bridge) Run(ctx context.Context, info models.Bridge, ch chan<- models.E defer conn.Close() // Notify connections and disconnections - ch <- models.Event{Name: "BridgeConnected", Payload: map[string]string{"bridgeId": strconv.Itoa(info.ID)}} + ch <- models.BridgeConnectedEvent(info) defer func() { - ch <- models.Event{Name: "BridgeDisconnected", Payload: map[string]string{"bridgeId": strconv.Itoa(info.ID)}} + ch <- models.BridgeDisconnectedEvent(info) }() // Start touch listener. This one should go down together with this one, though, so it needs a new context. diff --git a/models/event.go b/models/event.go index 2f3cd97..38f1a44 100644 --- a/models/event.go +++ b/models/event.go @@ -19,9 +19,21 @@ func (e *Event) HasPayload(key string) bool { return e.Payload != nil && e.Payload[key] != "" } +var ( + ENBridgeConnected = "BridgeConnected" + ENBridgeDisconnected = "BridgeDisconnected" +) + func BridgeConnectedEvent(bridge Bridge) Event { - e := Event{Name: "BridgeConnected"} - e.AddPayload("id", strconv.Itoa(bridge.ID)) + e := Event{Name: ENBridgeConnected} + e.AddPayload("bridgeId", strconv.Itoa(bridge.ID)) + + return e +} + +func BridgeDisconnectedEvent(bridge Bridge) Event { + e := Event{Name: ENBridgeDisconnected} + e.AddPayload("bridgeId", strconv.Itoa(bridge.ID)) return e } diff --git a/models/shared.go b/models/shared.go index cfe73fa..bada807 100644 --- a/models/shared.go +++ b/models/shared.go @@ -3,9 +3,9 @@ package models type ReferenceKind string var ( - RTDeviceID ReferenceKind = "DeviceID" - RTBridgeID ReferenceKind = "BridgeID" - RTTag ReferenceKind = "Tag" + RKDeviceID ReferenceKind = "DeviceID" + RKBridgeID ReferenceKind = "BridgeID" + RKTag ReferenceKind = "Tag" ) diff --git a/webui/src/primitives/Forms.tsx b/webui/src/primitives/Forms.tsx index 1a512d6..89881a7 100644 --- a/webui/src/primitives/Forms.tsx +++ b/webui/src/primitives/Forms.tsx @@ -1,4 +1,4 @@ -import React, {useLayoutEffect, useState} from 'react'; +import React, {useLayoutEffect, useMemo} from 'react'; // @ts-ignore import iro from "@jaames/iro"; @@ -11,7 +11,7 @@ interface ColorPickerProps { const randomId = () => Math.floor(Math.random() * 100000); export const HSColorPicker: React.FC = ({h, s, onChange}) => { - const [random] = useState(() => `color-picker-${randomId()}`); + const random = useMemo(() => `color-picker-${randomId()}`, []); useLayoutEffect(() => { // @ts-ignore diff --git a/webui/src/setupProxy.js b/webui/src/setupProxy.js new file mode 100644 index 0000000..ab74ee6 --- /dev/null +++ b/webui/src/setupProxy.js @@ -0,0 +1,8 @@ +const proxy = require("http-proxy-middleware"); + +let config = {url: "http://127.0.0.1:9000/"}; + +module.exports = function(app) { + app.use(proxy('/api/ws', { target: config.url, ws: true })); + app.use(proxy('/api', { target: config.url, ws: false })); +}; \ No newline at end of file