Skip to content

Commit

Permalink
Dev iteration: UI improvements, bug fixes, cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
hspaay committed Mar 22, 2024
1 parent 5fdc194 commit c6cf41c
Show file tree
Hide file tree
Showing 39 changed files with 388 additions and 226 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

HiveOT stands for the "Hive of Things". It provides a framework and services to collect and share IoT data with users.

The Hub for the
*Hive-of-Things* provides a secure core and plugins to view and operate IoT devices. The Hub securely mediates between IoT device 'Things', services, and users using a hub-and-spokes architecture. Users interact with Things via the Hub without connecting directly to the IoT devices or services. The Hub is based on the [W3C WoT TD 1.1 specification](https://www.w3.org/TR/wot-thing-description11/) and uses a NATS or MQTT message bus for secure communication.
The Hub for the *Hive-of-Things* provides a secure core and plugins to view and operate IoT devices. The Hub securely mediates between IoT device 'Things', services, and users using a hub-and-spokes architecture. Users interact with Things via the Hub without connecting directly to the IoT devices or services. The Hub is based on the [W3C WoT TD 1.1 specification](https://www.w3.org/TR/wot-thing-description11/) and uses a NATS or MQTT message bus for secure communication.

## Project Status

Expand All @@ -22,9 +21,16 @@ Completed core services:
Bindings

* 1-wire protocol binding using owserver-v2 gateway (bindings/owserver)
* insteon binding using isy99x gateway (bindings/isy99x)
* zwave protocol binding using zwavejs (bindings/zwavejs)
* web client using html/htmx and go templates (bindings/hiveoview) - in progress

Integrations

It is a bit early to look at integrations, but some interesting candidates are:
* plc4go (https://plc4x.apache.org/users/getting-started/plc4go.html)
* home assistant (https://www.home-assistant.io/)

## Audience

This project is aimed at software developers and system implementors that are working on secure IoT solutions. HiveOT users subscribe to the security mandate that IoT devices should be isolated from the internet and end-users should not have direct access to IoT devices. Instead, all access operates via the Hub.
Expand Down
26 changes: 18 additions & 8 deletions bindings/hiveoview/src/session/ClientSession.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package session

import (
"encoding/json"
"fmt"
"github.com/hiveot/hub/core/state/stateclient"
"github.com/hiveot/hub/lib/hubclient"
Expand Down Expand Up @@ -146,13 +147,20 @@ func (cs *ClientSession) onEvent(msg *things.ThingValue) {
thingAddr := fmt.Sprintf("%s/%s", msg.AgentID, msg.ThingID)
_ = cs.SendSSE(thingAddr, "")
} else if msg.Name == transports.EventNameProps {
// Publish sse event indicating one or more properties of a Thing has changed.
// The UI that displays this event can use this as a trigger to reload the
// fragment that displays this event:
// hx-trigger="sse:{{.Thing.AgentID}}/{{.Thing.ThingID}}/$properties"
// where $properties is transports.EventNameProps (can the ui use JS to get this constant?)
thingAddr := fmt.Sprintf("%s/%s/%s", msg.AgentID, msg.ThingID, msg.Name)
_ = cs.SendSSE(thingAddr, string(msg.Data))
// Publish an sse event for each of the properties
// The UI that displays this event can use this as a trigger to load the
// property value:
// hx-trigger="sse:{{.Thing.AgentID}}/{{.Thing.ThingID}}/{{k}}"
props := make(map[string]string)
err := json.Unmarshal(msg.Data, &props)
if err == nil {
for k, v := range props {
thingAddr := fmt.Sprintf("%s/%s/%s", msg.AgentID, msg.ThingID, k)
_ = cs.SendSSE(thingAddr, v)
thingAddr = fmt.Sprintf("%s/%s/%s/updated", msg.AgentID, msg.ThingID, k)
_ = cs.SendSSE(thingAddr, msg.GetUpdated())
}
}
} else {
// Publish sse event indicating the event affordance or value has changed.
// The UI that displays this event can use this as a trigger to reload the
Expand All @@ -162,8 +170,10 @@ func (cs *ClientSession) onEvent(msg *things.ThingValue) {
thingAddr := fmt.Sprintf("%s/%s/%s", msg.AgentID, msg.ThingID, msg.Name)
_ = cs.SendSSE(thingAddr, string(msg.Data))
// TODO: improve on this crude way to update the 'updated' field
// Can the value contain an object with a value and updated field instead?
// htmx sse-swap does allow cherry picking the content unfortunately.
thingAddr = fmt.Sprintf("%s/%s/%s/updated", msg.AgentID, msg.ThingID, msg.Name)
_ = cs.SendSSE(thingAddr, msg.Updated())
_ = cs.SendSSE(thingAddr, msg.GetUpdated())
}
}

Expand Down
Binary file added bindings/hiveoview/src/static/hiveot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions bindings/hiveoview/src/views/directory/directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
const DirectoryTemplate = "directory.gohtml"

type DirGroup struct {
Publisher string
Things []*things.TD
AgentID string
Things []*things.TD
}

type DirectoryData struct {
Expand All @@ -40,8 +40,8 @@ func sortByPublisher(tvList []things.ThingValue) *DirectoryData {
tplGroup, found := dirData.Groups[tv.SenderID]
if !found {
tplGroup = &DirGroup{
Publisher: tv.SenderID,
Things: make([]*things.TD, 0),
AgentID: tv.SenderID,
Things: make([]*things.TD, 0),
}
dirData.Groups[tv.SenderID] = tplGroup
}
Expand Down
6 changes: 3 additions & 3 deletions bindings/hiveoview/src/views/directory/directory.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<details>
<summary class="outline">
<span>Publisher:</span>
<span style="font-size: medium"><strong>{{.Publisher}}</strong></span>
<span style="font-size: medium"><strong>{{.AgentID}}</strong></span>
<span>- {{len .Things}} things</span>
</summary>

Expand All @@ -55,7 +55,7 @@
</li>

<!--=== for TD{} in .Things ===-->
{{$agentID := .Publisher}}
{{$agentID := .AgentID}}
{{range .Things}}
<li>
<div class="h-show-md">
Expand All @@ -77,7 +77,7 @@
<div class="h-show-sm">{{.GetAtTypeVocab}}</div>
<div class="h-show-lg">{{len .Events}} outputs</div>
<div class="h-show-lg">{{len .Actions}} actions</div>
<div class="h-show-xl">{{.GetAge}} ago</div>
<div class="h-show-xl">{{.GetUpdated}}</div>
</li>
{{end}}

Expand Down
8 changes: 0 additions & 8 deletions bindings/hiveoview/src/views/thing/editConfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,6 @@ func PostThingConfig(w http.ResponseWriter, r *http.Request) {
}

_ = mySession.SendSSE("notify", "success: Configuration '"+propKey+"' updated")
//
//// read the updated config value and update the UI fragments
//cl := historyclient.NewReadHistoryClient(hc)
//tvs, err := cl.GetLatest(agentID, thingID, []string{propKey})
//propAddr := fmt.Sprintf("%s/%s/%s", agentID, thingID, propKey)
//propVal := fmt.Sprintf("%.100s", tvs[propKey].Data)
//slog.Info("Updated value of prop", "propAddr", propAddr, "propVal", propVal)
//mySession.SendSSE(propAddr, propVal)

w.WriteHeader(http.StatusOK)

Expand Down
11 changes: 8 additions & 3 deletions bindings/hiveoview/src/views/thing/thingActions.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,16 @@
<span>{{$v.Title}}</span>
</div>
<div>{{$v.ActionType}}</div>
<div class="h-show-md">{{$.Values.ToString $k}}</div>
<div class="h-show-md"
sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}" hx-swap="innerHTML">
{{$.Values.ToString $k}}
</div>
<div class="h-show-lg">{{$v.Description}}</div>
<div class="h-show-md"
title="Updated: {{$.Values.Age $k}} ago by {{($.Values.SenderID $k)}}">
{{$.Values.Updated $k}}</div>
sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}/updated" hx-swap="innerHTML"
title="Updated: {{$.Values.GetUpdated $k}} by {{($.Values.SenderID $k)}}">
{{$.Values.GetUpdated $k}}
</div>
</li>
{{end}}
{{if not .TD.Actions}}
Expand Down
7 changes: 4 additions & 3 deletions bindings/hiveoview/src/views/thing/thingAttr.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@
<span>{{$v.Title}}</span>
</div>
<div>
<span sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}">
<span sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}" hx-swap="innerHTML">
{{$.Values.ToString $k}}
</span>
{{$v.UnitSymbol}}
</div>
<div class="h-show-lg">{{$v.Description}}</div>
<div class="h-show-md"
title="Updated: {{$.Values.Age $k}} ago by {{($.Values.SenderID $k)}}">
{{$.Values.Updated $k}}</div>
sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}/updated" hx-swap="innerHTML"
title="Updated: {{$.Values.GetUpdated $k}} by {{($.Values.SenderID $k)}}">
{{$.Values.GetUpdated $k}}</div>
</li>
{{end}}
</ul>
Expand Down
8 changes: 4 additions & 4 deletions bindings/hiveoview/src/views/thing/thingConfig.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@
>
<iconify-icon style="padding: 0 10px" icon="mdi:pencil"></iconify-icon>
{{/*replace show value on sse event*/}}
<span sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}"
title="age: {{$.Values.Age $k}}">
<span sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}" hx-swap="innerHTML">
{{$.Values.ToString $k}}
</span>
{{$v.UnitSymbol}}
Expand All @@ -43,9 +42,10 @@
<div class="h-show-md">{{$v.Default}}</div>
<div class="h-show-lg">{{$v.Description}}</div>
<div class="h-show-sm"
title="Updated: {{$.Values.Age $k}} ago by {{($.Values.SenderID $k)}}"
sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}/updated" hx-swap="innerHTML"
title="Updated: {{$.Values.GetUpdated $k}} by {{($.Values.SenderID $k)}}"
>
{{$.Values.Updated $k}}
{{$.Values.GetUpdated $k}}
</div>

</li>
Expand Down
1 change: 0 additions & 1 deletion bindings/hiveoview/src/views/thing/thingDetails.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
<label for="updated">Updated:</label>
<input id="updated" type="text" readonly
style="margin: 0; text-overflow: ellipsis;"
title="Updated: {{$.Thing.TD.GetAge}} ago"
placeholder="{{$.Thing.TD.GetUpdated}}"/>

<label for="agentID">Published by:</label>
Expand Down
2 changes: 1 addition & 1 deletion bindings/hiveoview/src/views/thing/thingEvents.gohtml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
{{/*TODO: improve on this rather crude way to refresh the derived 'updated' field when value changes?*/}}
<div sse-swap="{{$.AgentID}}/{{$.ThingID}}/{{$k}}/updated" hx-swap="innerHTML"
class="h-show-md"
>{{$.Values.Updated $k}}</div>
>{{$.Values.GetUpdated $k}}</div>
</li>
{{end}}
{{if not .TD.Events}}
Expand Down
25 changes: 0 additions & 25 deletions bindings/hiveoview/src/views/thing/thingEventsRow.gohtml

This file was deleted.

2 changes: 1 addition & 1 deletion bindings/hiveoview/test/Hiveov_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestMain(m *testing.M) {
func TestStartStop(t *testing.T) {
t.Log("--- TestStartStop ---")

svc := service.NewHiveovService(8080, true, nil, "")
svc := service.NewHiveovService(9999, true, nil, "")
hc1, err := testServer.AddConnectClient(
serviceID, authapi.ClientTypeService, authapi.ClientRoleService)
require.NoError(t, err)
Expand Down
10 changes: 5 additions & 5 deletions bindings/isy99x/service/IsyGatewayThing.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ type IsyGatewayThing struct {
// REST/SOAP/WS connection to the ISY hub
ic *IsyAPI

// The gateway thing ID
id string
// The gateway thingID
thingID string

// map of ISY product ID's
prodMap map[string]InsteonProduct
Expand Down Expand Up @@ -238,7 +238,7 @@ func (igw *IsyGatewayThing) GetIsyThingByNodeID(nodeID string) IIsyThing {

// GetID return the gateway thingID
func (igw *IsyGatewayThing) GetID() string {
return igw.id
return igw.thingID
}

// GetIsyThings returns a list of ISY devices for publishing TD or values as updated in
Expand Down Expand Up @@ -269,7 +269,7 @@ func (igw *IsyGatewayThing) GetTD() *things.TD {
return nil
}

td := things.NewTD(igw.id, igw.Configuration.DeviceSpecs.Model, vocab.ThingNetGateway)
td := things.NewTD(igw.thingID, igw.Configuration.DeviceSpecs.Model, vocab.ThingNetGateway)
td.Description = igw.Configuration.DeviceSpecs.Make + "-" + igw.Configuration.DeviceSpecs.Model

//--- device read-only attributes
Expand Down Expand Up @@ -330,7 +330,7 @@ func (igw *IsyGatewayThing) GetTD() *things.TD {
// This removes prior use nodes for a fresh start.
func (igw *IsyGatewayThing) Init(ic *IsyAPI) {
igw.ic = ic
igw.id = ic.GetID()
igw.thingID = ic.GetID()
igw.things = make(map[string]IIsyThing)
igw.propValues = things.NewPropertyValues()

Expand Down
6 changes: 4 additions & 2 deletions bindings/owserver/service/OWServerBinding.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const bindingMake = "make"

// OWServerBinding is the hub protocol binding plugin for capturing 1-wire OWServer V2 Data
type OWServerBinding struct {
thingID string

// Configuration of this protocol binding
config *config.OWServerConfig

Expand All @@ -48,8 +50,7 @@ type OWServerBinding struct {

// CreateBindingTD generates a TD document for this binding
func (svc *OWServerBinding) CreateBindingTD() *things.TD {
thingID := svc.hc.ClientID()
td := things.NewTD(thingID, "OWServer binding", vocab.ThingServiceAdapter)
td := things.NewTD(svc.thingID, "OWServer binding", vocab.ThingServiceAdapter)
td.Description = "Driver for the OWServer V2 Gateway 1-wire interface"

prop := td.AddProperty(bindingMake, vocab.PropDeviceMake,
Expand Down Expand Up @@ -94,6 +95,7 @@ func (svc *OWServerBinding) Start(hc *hubclient.HubClient) (err error) {
logging.SetLogging(svc.config.LogLevel, "")
}
svc.hc = hc
svc.thingID = hc.ClientID()
// Create the adapter for the OWServer 1-wire gateway
svc.edsAPI = eds.NewEdsAPI(
svc.config.OWServerURL, svc.config.OWServerLogin, svc.config.OWServerPassword)
Expand Down
22 changes: 2 additions & 20 deletions bindings/zwavejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,6 @@ TODO:
1. Include DataSchema in controller configurations for properties that aren't in the zwave-js vids.
1. Dimming duration is currently not supported

## ~~Build with pkg using tsc~~

!!! WARNING: this no longer works as it isn't compatible with ESM.

> Error [ERR_REQUIRE_ESM]: require() of ES Module node_modules/tslog/dist/cjs/index.js
old:
This needs:

* yarn
* nodejs v20+
* typescript compiler v4.9+ (tsc, included in snap's node)
* make tools
* pkg, tsc, tsc-alias, etc

Run 'yarn dist' and watch the magic unfold (...hopefully, this is a javascript build environment after all).

If all goes according to plan, a single binary is produced in the dist folder: dist/zwavejs

## Building with esbuild

This first step is just for testing the build process using esbuild. If this already fails then no use using pkg or the 'postject' node20+ injector. Note: one reason to take this step is to allow packages with es modules (axios) to work. pkg seems to not build correctly with code generated with tsc && tsc-alias.
Expand All @@ -54,7 +35,7 @@ Then run from the project root with:
* note1: --clientID is the client this runs under, eg 'testsvc' during testing. Default will be the binding name zwavejs.

## build a single executable using pkg and esbuild from zwave-js-ui
## build a single executable using pkg and esbuild.js from zwave-js-ui

NOTE: This uses the esbuild.js script from zwave-js-ui, which does some filename mangling to
get the externals to work. Not sure how it works but I also really don't care.
Expand All @@ -73,6 +54,7 @@ Installation needs the executable, an authentication token, and the CA certifica

The binding executable should be copied to the hiveot plugins folder, for example: ~/bin/hiveot/bin/plugins. The authentication token is generated by the launcher, or can be generated manually using the hubcli. The token file has the same name as the executable with the .token extension. The CA certificate is generated by the hubcli on startup and placed in certs/caCert.pem.


## Run

Before running the binding make sure the hub gateway is running. 'hubcli ls' lists the running processes.
Expand Down
3 changes: 2 additions & 1 deletion bindings/zwavejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"pkg": "./build.sh",
"esbuildold": "esbuild src/main.ts --bundle --platform=node --target=node20 --preserve-symlinks --external:\"prebuilds/*\" --external:./node_modules/zwave-js/package.json --external:./node_modules/@zwave-js/config/package.json --outfile=build/main.js",
"devold": "tsc && tsc-alias && ZWAVEJS_EXTERNAL_CONFIG=dist/cache node --preserve-symlinks build/src/main.js --clientID testsvc --home ~/bin/hiveot",
"devtsxold": "ZWAVEJS_EXTERNAL_CONFIG=dist/cache tsx --preserve-symlinks src/main.ts --clientID testsvc --home ~/bin/hiveot",
"debugtsx": "ZWAVEJS_EXTERNAL_CONFIG=dist/cache tsx --preserve-symlinks src/main.ts --clientID testsvc --home ~/bin/hiveot",
"distinstall": "cp dist/zwavejs ~/bin/hiveot/plugins",
"test": "tsc && tsc-alias && node --preserve-symlinks build/src/tests/hubconnect_test.js",
"testtsx": "tsx --preserve-symlinks src/tests/hubconnect_test.ts",
Expand Down Expand Up @@ -52,6 +52,7 @@
"nkeys.js": "^1.0.5",
"process": "^0.11.10",
"serialport": "^12.0.0",
"ts-node": "^10.9.2",
"tslog": "^4.9.2",
"ws": "^8.14.2",
"yaml": "^2.3.4",
Expand Down
Loading

0 comments on commit c6cf41c

Please sign in to comment.