Skip to content

Commit

Permalink
Remove redundant APIs (#52)
Browse files Browse the repository at this point in the history
As we move towards a more stable API:s I want to clean up some redundant API:s. I rather add more if the community wants it. After this PR the APIs will be more stable but not guaranteed not to be changed until first release. 

**BREAKING CHANGE!!**
Following API calls are removed.

- TurnOnAsync
- TurnOffAsync
- ToggleAsync
- Light / Lights

There are alternatives. Use `await Entity("light.some_light").TurnOn().ExecuteAsync();` or `CallService`instead.

If you still want to use these please copy the extensions methods I provided in my repo:
https://github.com/helto4real/hassio/blob/master/netdaemon/apps/Extensions/ServiceExtensions.cs
  • Loading branch information
helto4real committed Mar 28, 2020
1 parent 921b2c7 commit b5bbee8
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 234 deletions.
28 changes: 28 additions & 0 deletions src/App/NetDaemon.App/Common/ExtensionMethods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

using System.Collections.Generic;

namespace JoySoftware.HomeAssistant.NetDaemon.Common
{
/// <summary>
/// Useful extension methods used
/// </summary>
public static class Extensions
{
/// <summary>
/// Converts a valuepair to dynamic object
/// </summary>
/// <param name="attributeNameValuePair"></param>
public static dynamic ToDynamic(this (string name, object val)[] attributeNameValuePair)
{
// Convert the tuple name/value pair to tuple that can be serialized dynamically
var attributes = new FluentExpandoObject(true, true);
foreach (var (attribute, value) in attributeNameValuePair)
{
((IDictionary<string, object>)attributes).Add(attribute, value);
}

dynamic result = attributes;
return result;
}
}
}
48 changes: 45 additions & 3 deletions src/App/NetDaemon.App/Common/Fluent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -860,9 +860,9 @@ private async Task HandleAction(FluentAction fluentAction)
{
var task = fluentAction.ActionType switch
{
FluentActionType.TurnOff => _daemon.TurnOffAsync(entityId, attributes),
FluentActionType.TurnOn => _daemon.TurnOnAsync(entityId, attributes),
FluentActionType.Toggle => _daemon.ToggleAsync(entityId, attributes),
FluentActionType.TurnOff => TurnOffAsync(entityId, attributes),
FluentActionType.TurnOn => TurnOnAsync(entityId, attributes),
FluentActionType.Toggle => ToggleAsync(entityId, attributes),
FluentActionType.SetState => _daemon.SetState(entityId, fluentAction.State, attributes),
_ => throw new NotSupportedException($"Fluent action type not handled! {fluentAction.ActionType}")
};
Expand All @@ -871,6 +871,48 @@ private async Task HandleAction(FluentAction fluentAction)
// Wait for all tasks to complete or max 5 seconds
if (taskList.Count > 0) await Task.WhenAny(Task.WhenAll(taskList.ToArray()), Task.Delay(5000)).ConfigureAwait(false);
}

private Task ToggleAsync(string entityId, params (string name, object val)[] attributeNameValuePair)
{
// Get the domain if supported, else domain is homeassistant
string domain = GetDomainFromEntity(entityId);
// Use it if it is supported else use default "homeassistant" domain

// Use expando object as all other methods
dynamic attributes = attributeNameValuePair.ToDynamic();
// and add the entity id dynamically
attributes.entity_id = entityId;

return _daemon.CallService(domain, "toggle", attributes, false);
}

private Task TurnOffAsync(string entityId, params (string name, object val)[] attributeNameValuePair)
{
// Get the domain if supported, else domain is homeassistant
string domain = GetDomainFromEntity(entityId);
// Use it if it is supported else use default "homeassistant" domain

// Use expando object as all other methods
dynamic attributes = attributeNameValuePair.ToDynamic();
// and add the entity id dynamically
attributes.entity_id = entityId;

return _daemon.CallService(domain, "turn_off", attributes, false);
}

private Task TurnOnAsync(string entityId, params (string name, object val)[] attributeNameValuePair)
{
// Use default domain "homeassistant" if supported is missing
string domain = GetDomainFromEntity(entityId);
// Use it if it is supported else use default "homeassistant" domain

// Convert the value pairs to dynamic type
dynamic attributes = attributeNameValuePair.ToDynamic();
// and add the entity id dynamically
attributes.entity_id = entityId;

return _daemon.CallService(domain, "turn_on", attributes, false);
}
}

/// <summary>
Expand Down
28 changes: 0 additions & 28 deletions src/App/NetDaemon.App/Common/INetDaemon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,28 +166,6 @@ public interface INetDaemon
void ListenServiceCall(string domain, string service,
Func<dynamic?, Task> action);

/// <summary>
/// Turn on entity who support the service call
/// </summary>
/// <param name="entityId">The unique id of the entity</param>
/// <param name="attributes">Name/Value pair of the attribute</param>

Task TurnOnAsync(string entityId, params (string name, object val)[] attributes);

/// <summary>
/// Turn off entity who support the service call
/// </summary>
/// <param name="entityId">The unique id of the entity</param>
/// <param name="attributes">Name/Value pair of the attribute</param>
Task TurnOffAsync(string entityId, params (string name, object val)[] attributes);

/// <summary>
/// Toggle entity who support the service call
/// </summary>
/// <param name="entityId">The unique id of the entity</param>
/// <param name="attributes">Name/Value pair of the attribute</param>
Task ToggleAsync(string entityId, params (string name, object val)[] attributes);

/// <summary>
/// Set entity state
/// </summary>
Expand Down Expand Up @@ -273,12 +251,6 @@ public interface INetDaemon
/// <param name="func">The lambda expression selecting input select</param>
IFluentInputSelect InputSelects(Func<IEntityProperties, bool> func);

/// <summary>
/// Selects one or more light entities to do action on
/// </summary>
/// <param name="entityId">The unique id of the entity</param>
ILight Light(params string[] entityId);

/// <summary>
/// Selects one or more media player entities to do action on
/// </summary>
Expand Down
28 changes: 0 additions & 28 deletions src/App/NetDaemon.App/Common/NetDaemonApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,6 @@ public virtual Task InitializeAsync()
return Task.CompletedTask;
}

/// <inheritdoc/>
public ILight Light(params string[] entity)
{
_ = _daemon as INetDaemon ?? throw new NullReferenceException($"{nameof(_daemon)} cant be null!");
return _daemon!.Light(entity);
}

/// <inheritdoc/>
public void ListenEvent(string ev, Func<string, dynamic?, Task> action) => _daemon?.ListenEvent(ev, action);

Expand Down Expand Up @@ -265,27 +258,6 @@ public async Task RestoreAppStateAsync()
IsEnabled = appState == "on";
}

/// <inheritdoc/>
public Task ToggleAsync(string entityId, params (string name, object val)[] attributes)
{
_ = _daemon as INetDaemon ?? throw new NullReferenceException($"{nameof(_daemon)} cant be null!");
return _daemon!.ToggleAsync(entityId, attributes);
}

/// <inheritdoc/>
public Task TurnOffAsync(string entityId, params (string name, object val)[] attributes)
{
_ = _daemon as INetDaemon ?? throw new NullReferenceException($"{nameof(_daemon)} cant be null!");
return _daemon!.TurnOffAsync(entityId, attributes);
}

/// <inheritdoc/>
public Task TurnOnAsync(string entityId, params (string name, object val)[] attributes)
{
_ = _daemon as INetDaemon ?? throw new NullReferenceException($"{nameof(_daemon)} cant be null!");
return _daemon!.TurnOnAsync(entityId, attributes);
}

#region IDisposable Support

private bool disposedValue = false; // To detect redundant calls
Expand Down
2 changes: 0 additions & 2 deletions src/CustomApp/MyApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ private async Task OnTomasRoomChanged(string entityId, EntityState newState, Ent
Log("Tomas light changed!");
Log($"New state = {newState.State}");

await TurnOnAsync("light.vardagsrummet",
("brightness", "100"), ("color_temp", 123));

//await And("light.vardagsrummet")
// .TurnOn(
Expand Down
24 changes: 12 additions & 12 deletions src/Daemon/NetDaemon.Daemon/Daemon/ExtensionMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,17 @@ public static object ParseDataType(string state)
return state;
}

public static dynamic ToDynamic(this (string name, object val)[] attributeNameValuePair)
{
// Convert the tuple name/value pair to tuple that can be serialized dynamically
var attributes = new FluentExpandoObject(true, true);
foreach (var (attribute, value) in attributeNameValuePair)
{
((IDictionary<string, object>)attributes).Add(attribute, value);
}

dynamic result = attributes;
return result;
}
// public static dynamic ToDynamic(this (string name, object val)[] attributeNameValuePair)
// {
// // Convert the tuple name/value pair to tuple that can be serialized dynamically
// var attributes = new FluentExpandoObject(true, true);
// foreach (var (attribute, value) in attributeNameValuePair)
// {
// ((IDictionary<string, object>)attributes).Add(attribute, value);
// }

// dynamic result = attributes;
// return result;
// }
}
}
67 changes: 2 additions & 65 deletions src/Daemon/NetDaemon.Daemon/Daemon/NetDaemonHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,33 +124,13 @@ public IEntity Entities(Func<IEntityProperties, bool> func)
: null;
}

public ILight Light(params string[] entity)
{
var entityList = new List<string>(entity.Length);
foreach (string e in entity)
{
// Add the domain light if missing domain in id
entityList.Add(!e.Contains('.') ? string.Concat("light.", e) : e);
}

return new EntityManager(entityList.ToArray(), this);
}

public IEntity Lights(Func<IEntityProperties, bool> func)
{
IEnumerable<IEntityProperties> x = State.Where(func).Where(n => n.EntityId.Contains("light."));

return new EntityManager(x.Select(n => n.EntityId).ToArray(), this);
}

public void ListenEvent(string ev, Func<string, dynamic, Task> action) => _eventActions.Add((ev, action));

public void ListenEvent(Func<FluentEventProperty, bool> funcSelector, Func<string, dynamic, Task> func) => _eventFunctionList.Add((funcSelector, func));

public void ListenServiceCall(string domain, string service, Func<dynamic?, Task> action)
=> _serviceCallFunctionList.Add((domain.ToLowerInvariant(), service.ToLowerInvariant(), action));


/// <inheritdoc/>
public string? ListenState(string pattern,
Func<string, EntityState?, EntityState?, Task> action)
Expand Down Expand Up @@ -350,51 +330,6 @@ public async Task Stop()

public ITime Timer() => new Common.TimeManager(this);

public Task ToggleAsync(string entityId, params (string name, object val)[] attributeNameValuePair)
{
// Get the domain if supported, else domain is homeassistant
string domain = GetDomainFromEntity(entityId);
// Use it if it is supported else use default "homeassistant" domain
domain = _supportedDomainsForTurnOnOff.Contains(domain) ? domain : "homeassistant";

// Use expando object as all other methods
dynamic attributes = attributeNameValuePair.ToDynamic();
// and add the entity id dynamically
attributes.entity_id = entityId;

return _hassClient.CallService(domain, "toggle", attributes, false);
}

public Task TurnOffAsync(string entityId, params (string name, object val)[] attributeNameValuePair)
{
// Get the domain if supported, else domain is homeassistant
string domain = GetDomainFromEntity(entityId);
// Use it if it is supported else use default "homeassistant" domain
domain = _supportedDomainsForTurnOnOff.Contains(domain) ? domain : "homeassistant";

// Use expando object as all other methods
dynamic attributes = attributeNameValuePair.ToDynamic();
// and add the entity id dynamically
attributes.entity_id = entityId;

return _hassClient.CallService(domain, "turn_off", attributes, false);
}

public Task TurnOnAsync(string entityId, params (string name, object val)[] attributeNameValuePair)
{
// Use default domain "homeassistant" if supported is missing
string domain = GetDomainFromEntity(entityId);
// Use it if it is supported else use default "homeassistant" domain
domain = _supportedDomainsForTurnOnOff.Contains(domain) ? domain : "homeassistant";

// Convert the value pairs to dynamic type
dynamic attributes = attributeNameValuePair.ToDynamic();
// and add the entity id dynamically
attributes.entity_id = entityId;

return _hassClient.CallService(domain, "turn_on", attributes, false);
}

protected virtual async Task HandleNewEvent(HassEvent hassEvent, CancellationToken token)
{
if (hassEvent.EventType == "state_changed")
Expand Down Expand Up @@ -535,7 +470,9 @@ private async Task HandleTextToSpeechMessages(CancellationToken cancellationToke
attributes.message = message;
await _hassClient.CallService("tts", "google_cloud_say", attributes, true).ConfigureAwait(false);
await Task.Delay(InternalDelayTimeForTts).ConfigureAwait(false); // Wait 2 seconds to wait for status to complete

EntityState? currentPlayState = GetState(entityId);

if (currentPlayState != null && currentPlayState.Attribute?.media_duration != null)
{
int delayInMilliSeconds = (int)Math.Round(currentPlayState?.Attribute?.media_duration * 1000) - InternalDelayTimeForTts;
Expand Down
58 changes: 0 additions & 58 deletions tests/NetDaemon.Daemon.Tests/Demon/NetDaemonHostTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,64 +354,6 @@ public async Task CancelChangedStateForSubscriptionWillNotMakeCallback()
Assert.Empty(DefaultDaemonHost.InternalStateActions);
}

[Fact]
public async Task ToggleAsyncWithLightCallsSendMessageWithCorrectEntityId()
{
// ARRANGE
var excpectedAttributes = new ExpandoObject();
((IDictionary<string, object>)excpectedAttributes)["entity_id"] = "light.correct_entity";

// ACT
await DefaultDaemonHost.ToggleAsync("light.correct_entity");

// ASSERT

DefaultHassClientMock.Verify(n => n.CallService("light", "toggle", excpectedAttributes, It.IsAny<bool>()));
}

[Fact]
public async Task TurnOffAsyncWithLightCallsSendMessageWithCorrectEntityId()
{
// ARRANGE
var (_, expectedAttributes) = GetDynamicObject(
("entity_id", "light.correct_entity")
);

// ACT
await DefaultDaemonHost.TurnOffAsync("light.correct_entity");

// ASSERT

DefaultHassClientMock.Verify(n => n.CallService("light", "turn_off", expectedAttributes, It.IsAny<bool>()));
}

[Fact]
public async Task TurnOnAsyncWithErrorEntityIdThrowsApplicationException()
{
// ARRANGE

// ACT
var ex = await Assert.ThrowsAsync<ApplicationException>(async () => await DefaultDaemonHost.TurnOnAsync("light!correct_entity"));

Assert.Equal("entity_id is mal formatted light!correct_entity", ex.Message);
}

[Fact]
public async Task TurnOnAsyncWithLightCallsSendMessageWithCorrectEntityId()
{
// ARRANGE
var (_, expectedAttributes) = GetDynamicObject(
("entity_id", "light.correct_entity")
);

// ACT
await DefaultDaemonHost.TurnOnAsync("light.correct_entity");

// ASSERT

DefaultHassClientMock.Verify(n => n.CallService("light", "turn_on", expectedAttributes, It.IsAny<bool>()));
}

[Fact]
public async Task CallServiceEventShouldCallCorrectFunction()
{
Expand Down
Loading

0 comments on commit b5bbee8

Please sign in to comment.