Skip to content

Commit

Permalink
Merge pull request #25 from iloveicedgreentea/develop
Browse files Browse the repository at this point in the history
Better logging for debug
  • Loading branch information
iloveicedgreentea authored Aug 28, 2023
2 parents c7263f0 + 0b3251b commit c322add
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 72 deletions.
13 changes: 13 additions & 0 deletions ezbeq/ezbeq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,19 @@ func TestSearchCatalog(t *testing.T) {
expectedDigest: "f7e8c32e58b372f1ea410165607bc1f6b3f589a832fda87edaa32a17715438f7",
expectedMvAdjust: 0.0,
},
{
// spiderman universe
m: models.SearchRequest{
TMDB: "56292",
Year: 2011,
Codec: "TrueHD 7.1",
PreferredAuthor: "none",
Edition: "",
},
expectedEdition: "",
expectedDigest: "f7e8c32e58b372f1ea410165607bc1f6b3f589a832fda87edaa32a17715438f7",
expectedMvAdjust: 0.0,
},
}

for _, tc := range tt {
Expand Down
69 changes: 47 additions & 22 deletions handlers/plex_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,44 @@ func mediaPause(vip *viper.Viper, beqClient *ezbeq.BeqClient, haClient *homeassi
}
}

// readAttrAndWait is a generic func to read attr from HA
func readAttrAndWait(waitTime int, entName string, entType string, attrResp homeassistant.HAAttributeResponse, haClient *homeassistant.HomeAssistantClient) (bool, error) {
var err error
var isSignal bool

for i := 0; i < waitTime; i++ {
isSignal, err = haClient.ReadAttributes(entName, attrResp, entType)
if isSignal {
log.Debug("HDMI sync complete")
return isSignal, nil
}
if err != nil {
log.Errorf("Error reading envy attributes: %v", err)
return false, err
}
// otherwise continue
time.Sleep(1 * time.Second)
}
if err != nil {
log.Errorf("Error reading envy attributes: %v", err)
return false, err
}

return false, err

}

// waitForHDMISync will wait until the envy reports a signal to assume hdmi sync. No API to do this with denon afaik
func waitForHDMISync(wg *sync.WaitGroup, skipActions *bool, haClient *homeassistant.HomeAssistantClient, PlexClient *plex.PlexClient, vip *viper.Viper) {
if !vip.GetBool("ezbeq.waitforHDMIsync") {
if !vip.GetBool("signal.waitforHDMIsync") {
wg.Done()
return
}

log.Debug("Running HDMI sync wait")
defer func() {
// play item no matter what
// TODO: fix plex api
err := PlexClient.PlayPlex()
if err != nil {
log.Errorf("Error playing plex: %v", err)
Expand All @@ -165,10 +193,9 @@ func waitForHDMISync(wg *sync.WaitGroup, skipActions *bool, haClient *homeassist
wg.Done()
}()

// TODO: Explore Denon SD? -> SDNO, SDHMDI

signalSource := vip.GetString("signal.source")
var err error
var isSignal bool
var signal bool

// pause plex
log.Debug("pausing plex")
Expand All @@ -178,24 +205,22 @@ func waitForHDMISync(wg *sync.WaitGroup, skipActions *bool, haClient *homeassist
return
}

// read envy attributes until its not nosignal
// wait up to 30s to readEnvyAttributes
for i := 0; i < 30; i++ {
isSignal, err = haClient.ReadEnvyAttributes()
if isSignal {
log.Debug("HDMI sync complete")
return
}
if err != nil {
log.Errorf("Error reading envy attributes: %v", err)
return
}
// otherwise continue
time.Sleep(1 * time.Second)
switch signalSource {
case "envy":
// read envy attributes until its not nosignal
signal, err = readAttrAndWait(30, haClient.EnvyEntityName, "remote", &models.HAEnvyResponse{}, haClient)
case "jvc":
// read jvc attributes until its not nosignal
signal, err = readAttrAndWait(30, haClient.JVCEntityName, "remote", &models.HAjvcResponse{}, haClient)
case "sensor":
signal, err = readAttrAndWait(30, haClient.BinaryName, "binary_sensor", &models.HABinaryResponse{}, haClient)
default:
log.Errorf("waitforHDMIsync enabled but no valid source provided: %v", signalSource)
}

log.Debugf("HDMI Signal value is %v", signal)
if err != nil {
log.Errorf("Error reading envy attributes: %v", err)
return
log.Errorf("error getting HDMI signal: %v", err)
}

}
Expand All @@ -215,7 +240,7 @@ func mediaPlay(client *plex.PlexClient, vip *viper.Viper, beqClient *ezbeq.BeqCl
// if not using denoncodec, do this in background
if !useDenonCodec {
wg.Add(1)
// TODO: get this working
// TODO: test this
// sets skipActions to false on completion
go waitForHDMISync(wg, skipActions, haClient, client, vip)
}
Expand Down Expand Up @@ -599,7 +624,7 @@ func PlexWorker(plexChan <-chan models.PlexWebhookPayload, vip *viper.Viper) {

if vip.GetBool("homeAssistant.enabled") {
log.Info("Started with HA enabled")
haClient = homeassistant.NewClient(vip.GetString("homeAssistant.url"), vip.GetString("homeAssistant.port"), vip.GetString("homeAssistant.token"), vip.GetString("homeAssistant.envyName"))
haClient = homeassistant.NewClient(vip.GetString("homeAssistant.url"), vip.GetString("homeAssistant.port"), vip.GetString("homeAssistant.token"), vip.GetString("homeAssistant.envyRemoteName"), vip.GetString("homeAssistant.jvcRemoteName"), vip.GetString("homeAssistant.binarySensorName"))
}
if vip.GetBool("ezbeq.useAVRCodecSearch") {
log.Info("Started with AVR codec search enabled")
Expand Down
51 changes: 33 additions & 18 deletions homeassistant/ha.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,24 @@ import (
var log = logger.GetLogger()

type HomeAssistantClient struct {
ServerURL string
Port string
Token string
HTTPClient http.Client
ServerURL string
Port string
Token string
HTTPClient http.Client
EnvyEntityName string
JVCEntityName string
BinaryName string
}

// // A client to interface with home assistant
func NewClient(url, port string, token string, envyName string) *HomeAssistantClient {
func NewClient(url, port string, token string, envyName string, jvcName string, binaryName string) *HomeAssistantClient {
return &HomeAssistantClient{
ServerURL: url,
Port: port,
Token: token,
ServerURL: url,
Port: port,
Token: token,
EnvyEntityName: envyName,
JVCEntityName: jvcName,
BinaryName: binaryName,
HTTPClient: http.Client{
Timeout: 5 * time.Second,
},
Expand Down Expand Up @@ -123,23 +127,34 @@ func (c *HomeAssistantClient) SendNotification(msg string, endpointName string)
return err
}

// HAAttributeResponse is an interface for anything that implements these functions
type HAAttributeResponse interface {
GetState() string
GetSignalStatus() bool
}


// ReadEnvyAttributes returns true if there is a signal
func (c *HomeAssistantClient) ReadEnvyAttributes() (bool, error) {
endpoint := fmt.Sprintf("/api/states/remote.%s", c.EnvyEntityName)
// ReadAttributes generic function to read attribute. entType remote || binary_sensor
func (c *HomeAssistantClient) ReadAttributes(entityName string, respObj HAAttributeResponse, entType string) (bool, error) {
endpoint := fmt.Sprintf("/api/states/%s.%s", entType, entityName)
resp, err := c.doRequest(endpoint, nil, http.MethodGet)
if err != nil {
return false, err
}
log.Debugf("Response: %s", resp)

// unmarshal
var envyResp models.HAEnvyResponse
err = json.Unmarshal(resp, &envyResp)
if envyResp.State == "off" {
return false, fmt.Errorf("envy state is %s", envyResp.State)
err = json.Unmarshal(resp, respObj)

switch entType {
case "remote":
if respObj.GetState() == "off" {
return false, fmt.Errorf("envy state is %s", respObj.GetState())
}

return respObj.GetSignalStatus(), err
case "binary_sensor":
return respObj.GetState() == "on", err
default:
return false, err
}

return envyResp.Attributes.NoSignal, err
}
88 changes: 64 additions & 24 deletions homeassistant/ha_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,30 @@ package homeassistant

import (
// "strings"
"strings"
"testing"

"github.com/iloveicedgreentea/go-plex/models"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)

// make sure script can trigger
func TestScriptTrigger(t *testing.T) {

func testSetup() (*viper.Viper, *HomeAssistantClient, error) {
v := viper.New()
v.SetConfigFile("../config.json")
err := v.ReadInConfig()
if err != nil {
t.Fatal(err)
}
haClient := NewClient(v.GetString("homeAssistant.url"), v.GetString("homeAssistant.port"), v.GetString("homeAssistant.token"), v.GetString("homeAssistant.envyRemoteName"), v.GetString("homeAssistant.jvcRemoteName"), v.GetString("homeAssistant.binarySensorName"))

return v, haClient, err
}

// make sure script can trigger
func TestScriptTrigger(t *testing.T) {

_, haClient, err := testSetup()
assert.NoError(t, err)

// trigger an empty script to verify client
haClient := NewClient(v.GetString("homeAssistant.url"), v.GetString("homeAssistant.port"), v.GetString("homeAssistant.token"), v.GetString("homeAssistant.envyName"))
err = haClient.TriggerScript("test")
assert.NoError(t, err)

Expand All @@ -28,15 +34,10 @@ func TestScriptTrigger(t *testing.T) {
// this tests an actual light
func TestLightTrigger(t *testing.T) {
t.Skip()
v := viper.New()
v.SetConfigFile("../config.json")
err := v.ReadInConfig()
if err != nil {
t.Fatal(err)
}
_, haClient, err := testSetup()
assert.NoError(t, err)

// trigger light and switch
haClient := NewClient(v.GetString("homeAssistant.url"), v.GetString("homeAssistant.port"), v.GetString("homeAssistant.token"), v.GetString("homeAssistant.envyName"))

err = haClient.SwitchLight("light", "caseta_r_wireless_in_wall_dimmer", "off")
assert.NoError(t, err)
Expand All @@ -48,17 +49,56 @@ func TestLightTrigger(t *testing.T) {

// test sending a real notification
func TestNotification(t *testing.T) {

v := viper.New()
v.SetConfigFile("../config.json")
err := v.ReadInConfig()
if err != nil {
t.Fatal(err)
}

// trigger light and switch
haClient := NewClient(v.GetString("homeAssistant.url"), v.GetString("homeAssistant.port"), v.GetString("homeAssistant.token"), v.GetString("homeAssistant.envyName"))
v, haClient, err := testSetup()
assert.NoError(t, err)

// trigger light and switch
err = haClient.SendNotification("test from go-plex", v.GetString("ezbeq.notifyEndpointName"))
assert.NoError(t, err)
}
}
func TestReadAttributes(t *testing.T) {
_, haClient, err := testSetup()
assert.NoError(t, err)

type testStruct struct {
entName string
test HAAttributeResponse
entType string
}
tt := []testStruct{
{
entName: haClient.JVCEntityName,
test: &models.HAjvcResponse{},
entType: "remote",
},
{
entName: haClient.EnvyEntityName,
test: &models.HAEnvyResponse{},
entType: "remote",
},
{
entName: haClient.BinaryName,
test: &models.HABinaryResponse{},
entType: "binary_sensor",
},
}

for _, k := range tt {
k := k

signal, err := haClient.ReadAttributes(k.entName, k.test, k.entType)
t.Log(k.entName, signal)

// if its off, we expect an error
if err != nil {
if strings.Contains(err.Error(), "state is off") {
t.Logf("%s is off", k.entName)
assert.Equal(t, false, signal)
} else {
t.Error(err)
}
}

}
}
38 changes: 35 additions & 3 deletions models/homeassistant.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,40 @@ type HomeAssistantWebhookPayload struct {
type HAEnvyResponse struct {
EntityID string `json:"entity_id"`
State string `json:"state"`
Attributes Attributes `json:"attributes"`
Attributes EnvyAttributes `json:"attributes"`
}
type Attributes struct {
NoSignal bool `json:"is_signal"`
type EnvyAttributes struct {
SignalStatus bool `json:"is_signal"`
}
type HAjvcResponse struct {
EntityID string `json:"entity_id"`
State string `json:"state"`
Attributes JVCAttributes `json:"attributes"`
}
type JVCAttributes struct {
SignalStatus bool `json:"signal_status"`
}

type HABinaryResponse struct {
State string `json:"state"`
}

func (r *HABinaryResponse) GetState() string {
return r.State
}
func (r *HABinaryResponse) GetSignalStatus() bool {
return false
}

func (r *HAEnvyResponse) GetState() string {
return r.State
}
func (r *HAEnvyResponse) GetSignalStatus() bool {
return r.Attributes.SignalStatus
}
func (r *HAjvcResponse) GetState() string {
return r.State
}
func (r *HAjvcResponse) GetSignalStatus() bool {
return r.Attributes.SignalStatus
}
Loading

0 comments on commit c322add

Please sign in to comment.