mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 13:47:35 +00:00
Adds simulated sensor (#12539)
* Create simulated.py * Create test_simulated.py * Update .coveragerc * Drop numpy and fix attributes Drop numpy and fix attributes to be machine readble * Update test_simulated.py * Update simulated.py * Update test_simulated.py * Update simulated.py * Update test_simulated.py * Update simulated.py * Update simulated.py * Update test_simulated.py * Update simulated.py * Fix default random seed error * Update simulated.py * Addresses balloob comments * Update simulated.py
This commit is contained in:
parent
6e6ae173fd
commit
bf41674e06
@ -624,6 +624,7 @@ omit =
|
||||
homeassistant/components/sensor/serial.py
|
||||
homeassistant/components/sensor/serial_pm.py
|
||||
homeassistant/components/sensor/shodan.py
|
||||
homeassistant/components/sensor/simulated.py
|
||||
homeassistant/components/sensor/skybeacon.py
|
||||
homeassistant/components/sensor/sma.py
|
||||
homeassistant/components/sensor/snmp.py
|
||||
|
146
homeassistant/components/sensor/simulated.py
Normal file
146
homeassistant/components/sensor/simulated.py
Normal file
@ -0,0 +1,146 @@
|
||||
"""
|
||||
Adds a simulated sensor.
|
||||
|
||||
For more details about this platform, refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.simulated/
|
||||
"""
|
||||
import asyncio
|
||||
import datetime as datetime
|
||||
import math
|
||||
from random import Random
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
SCAN_INTERVAL = datetime.timedelta(seconds=1)
|
||||
ICON = 'mdi:chart-line'
|
||||
|
||||
CONF_UNIT = 'unit'
|
||||
CONF_AMP = 'amplitude'
|
||||
CONF_MEAN = 'mean'
|
||||
CONF_PERIOD = 'period'
|
||||
CONF_PHASE = 'phase'
|
||||
CONF_FWHM = 'spread'
|
||||
CONF_SEED = 'seed'
|
||||
|
||||
DEFAULT_NAME = 'simulated'
|
||||
DEFAULT_UNIT = 'value'
|
||||
DEFAULT_AMP = 1
|
||||
DEFAULT_MEAN = 0
|
||||
DEFAULT_PERIOD = 60
|
||||
DEFAULT_PHASE = 0
|
||||
DEFAULT_FWHM = 0
|
||||
DEFAULT_SEED = 999
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_UNIT, default=DEFAULT_UNIT): cv.string,
|
||||
vol.Optional(CONF_AMP, default=DEFAULT_AMP): vol.Coerce(float),
|
||||
vol.Optional(CONF_MEAN, default=DEFAULT_MEAN): vol.Coerce(float),
|
||||
vol.Optional(CONF_PERIOD, default=DEFAULT_PERIOD): cv.positive_int,
|
||||
vol.Optional(CONF_PHASE, default=DEFAULT_PHASE): vol.Coerce(float),
|
||||
vol.Optional(CONF_FWHM, default=DEFAULT_FWHM): vol.Coerce(float),
|
||||
vol.Optional(CONF_SEED, default=DEFAULT_SEED): cv.positive_int,
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Set up the simulated sensor."""
|
||||
name = config.get(CONF_NAME)
|
||||
unit = config.get(CONF_UNIT)
|
||||
amp = config.get(CONF_AMP)
|
||||
mean = config.get(CONF_MEAN)
|
||||
period = config.get(CONF_PERIOD)
|
||||
phase = config.get(CONF_PHASE)
|
||||
fwhm = config.get(CONF_FWHM)
|
||||
seed = config.get(CONF_SEED)
|
||||
|
||||
sensor = SimulatedSensor(
|
||||
name, unit, amp, mean, period, phase, fwhm, seed
|
||||
)
|
||||
add_devices([sensor], True)
|
||||
|
||||
|
||||
class SimulatedSensor(Entity):
|
||||
"""Class for simulated sensor."""
|
||||
|
||||
def __init__(self, name, unit, amp, mean, period, phase, fwhm, seed):
|
||||
"""Init the class."""
|
||||
self._name = name
|
||||
self._unit = unit
|
||||
self._amp = amp
|
||||
self._mean = mean
|
||||
self._period = period
|
||||
self._phase = phase # phase in degrees
|
||||
self._fwhm = fwhm
|
||||
self._seed = seed
|
||||
self._random = Random(seed) # A local seeded Random
|
||||
self._start_time = dt_util.utcnow()
|
||||
self._state = None
|
||||
|
||||
def time_delta(self):
|
||||
""""Return the time delta."""
|
||||
dt0 = self._start_time
|
||||
dt1 = dt_util.utcnow()
|
||||
return dt1 - dt0
|
||||
|
||||
def signal_calc(self):
|
||||
"""Calculate the signal."""
|
||||
mean = self._mean
|
||||
amp = self._amp
|
||||
time_delta = self.time_delta().total_seconds()*1e6 # to milliseconds
|
||||
period = self._period*1e6 # to milliseconds
|
||||
fwhm = self._fwhm/2
|
||||
phase = math.radians(self._phase)
|
||||
if period == 0:
|
||||
periodic = 0
|
||||
else:
|
||||
periodic = amp * (math.sin((2*math.pi*time_delta/period) + phase))
|
||||
noise = self._random.gauss(mu=0, sigma=fwhm)
|
||||
return mean + periodic + noise
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Update the sensor."""
|
||||
self._state = self.signal_calc()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Icon to use in the frontend, if any."""
|
||||
return ICON
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit this state is expressed in."""
|
||||
return self._unit
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return other details about the sensor state."""
|
||||
attr = {
|
||||
'amplitude': self._amp,
|
||||
'mean': self._mean,
|
||||
'period': self._period,
|
||||
'phase': self._phase,
|
||||
'spread': self._fwhm,
|
||||
'seed': self._seed,
|
||||
}
|
||||
return attr
|
50
tests/components/sensor/test_simulated.py
Normal file
50
tests/components/sensor/test_simulated.py
Normal file
@ -0,0 +1,50 @@
|
||||
"""The tests for the simulated sensor."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.components.sensor.simulated import (
|
||||
CONF_UNIT, CONF_AMP, CONF_MEAN, CONF_PERIOD, CONF_PHASE, CONF_FWHM,
|
||||
CONF_SEED, DEFAULT_NAME, DEFAULT_AMP, DEFAULT_MEAN,
|
||||
DEFAULT_PHASE, DEFAULT_FWHM, DEFAULT_SEED)
|
||||
from homeassistant.const import CONF_FRIENDLY_NAME
|
||||
from homeassistant.setup import setup_component
|
||||
from tests.common import get_test_home_assistant
|
||||
|
||||
|
||||
class TestSimulatedSensor(unittest.TestCase):
|
||||
"""Test the simulated sensor."""
|
||||
|
||||
def setup_method(self, method):
|
||||
"""Set up things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
def teardown_method(self, method):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_default_config(self):
|
||||
"""Test default config."""
|
||||
config = {
|
||||
'sensor': {
|
||||
'platform': 'simulated'}
|
||||
}
|
||||
self.assertTrue(
|
||||
setup_component(self.hass, 'sensor', config))
|
||||
self.hass.block_till_done()
|
||||
assert len(self.hass.states.entity_ids()) == 1
|
||||
state = self.hass.states.get('sensor.simulated')
|
||||
assert state.attributes.get(
|
||||
CONF_FRIENDLY_NAME) == DEFAULT_NAME
|
||||
assert state.attributes.get(
|
||||
CONF_AMP) == DEFAULT_AMP
|
||||
assert state.attributes.get(
|
||||
CONF_UNIT) is None
|
||||
assert state.attributes.get(
|
||||
CONF_MEAN) == DEFAULT_MEAN
|
||||
assert state.attributes.get(
|
||||
CONF_PERIOD) == 60.0
|
||||
assert state.attributes.get(
|
||||
CONF_PHASE) == DEFAULT_PHASE
|
||||
assert state.attributes.get(
|
||||
CONF_FWHM) == DEFAULT_FWHM
|
||||
assert state.attributes.get(
|
||||
CONF_SEED) == DEFAULT_SEED
|
Loading…
x
Reference in New Issue
Block a user