...
 
Commits (9)
This diff is collapsed.
......@@ -52,7 +52,6 @@ class ServiceConfig implements CombineInterface
);
};
$container[Client::class] = function (Container $container): Client {
$conf = $container[Combine::CONFIG]['mosquitto'];
$client = new Client();
......
......@@ -268,7 +268,7 @@ class HeatingManagementService
{
$this->logger->info("Brenner wird EIN geschaltet");
$this->saveLastBurnerStatus(true);
$this->relayService->setRelay($this->config[self::RELAY_CONFIG][RelayState::BURNER_KEY], true);
$this->relayService->setRelay($this->config[self::RELAY_CONFIG][RelayState::BURNER_KEY], RelayService::IO_VALUE_ON);
}
public function saveLastBurnerStatus(bool $status)
......@@ -280,7 +280,7 @@ class HeatingManagementService
{
$this->logger->info("Brenner wird AUS geschaltet");
$this->saveLastBurnerStatus(false);
$this->relayService->setRelay($this->config[self::RELAY_CONFIG][RelayState::BURNER_KEY], false);
$this->relayService->setRelay($this->config[self::RELAY_CONFIG][RelayState::BURNER_KEY], RelayService::IO_VALUE_OFF);
}
public function controlPump()
......@@ -322,7 +322,7 @@ class HeatingManagementService
{
$this->logger->info("Pumpe wird EIN geschaltet");
$this->saveLastPumpStatus(true);
$this->relayService->setRelay($this->config[self::RELAY_CONFIG][RelayState::PUMP_KEY], true);
$this->relayService->setRelay($this->config[self::RELAY_CONFIG][RelayState::PUMP_KEY], RelayService::IO_VALUE_ON);
}
public function saveLastPumpStatus(bool $status)
......@@ -351,6 +351,6 @@ class HeatingManagementService
{
$this->logger->info("Pumpe wird AUS geschaltet");
$this->saveLastPumpStatus(false);
$this->relayService->setRelay($this->config[self::RELAY_CONFIG][RelayState::PUMP_KEY], false);
$this->relayService->setRelay($this->config[self::RELAY_CONFIG][RelayState::PUMP_KEY], RelayService::IO_VALUE_OFF);
}
}
<?php
declare(strict_types=1);
namespace HeatingControl\Service;
......@@ -6,6 +7,9 @@ use HeatingControl\HeatingException;
class RelayService
{
const IO_VALUE_ON = true;
const IO_VALUE_OFF = false;
const CONFIG_KEY = 'relayIds';
// https://www.waveshare.com/wiki/RPi_Relay_Board#Interface_description see BCM
......@@ -62,22 +66,21 @@ class RelayService
/**
* @param string $gpio
* @param bool $newStatus
* @param bool $newStatus self::IO_VALUE_ON, self::IO_VALUE_OFF
* @return bool
* @throws HeatingException
*/
public function setRelay(string $gpio, bool $newStatus)
{
$this->isGpioValid($gpio);
if ($newStatus === true) {
$value = '1';
} else {
$value = '0';
if (!in_array($newStatus, [self::IO_VALUE_ON, self::IO_VALUE_OFF])) {
throw new HeatingException('Invalid Relay Status');
}
$cmd = 'echo ' . escapeshellarg($value) . ' > /sys/class/gpio/' . escapeshellcmd($gpio) . '/value';
$this->commandService->exec(
$cmd = 'echo ' . escapeshellarg((string)$newStatus) . ' > /sys/class/gpio/' . escapeshellcmd($gpio) . '/value';
$status = $this->commandService->exec(
$cmd
);
return ($status !== null);
}
}
......@@ -13,8 +13,6 @@ use Psr\Log\LoggerInterface;
*/
class SensorStoreMqqt implements SensorStoreInterface
{
const SENSOR_PREFIX = 'homeassistant/sensor/';
/** @var Client */
protected $client;
......@@ -34,27 +32,10 @@ class SensorStoreMqqt implements SensorStoreInterface
{
foreach ($sensorValues as $sensorValue) {
$this->getClient()->loop(1000);
$sensorStateUrl = self::SENSOR_PREFIX . $sensorValue->getId() . '/heating/state';
$discoveryJson = json_encode(
[
'name' => $sensorValue->getName(),
'device_class' => $sensorValue->getDeviceClass(),
'state_topic' => $sensorStateUrl,
'unit_of_measurement' => $sensorValue->getUnit()
]
);
$discoveryUrl = self::SENSOR_PREFIX . $sensorValue->getId() . '/heating/config';
$this->getClient()->publish(
$discoveryUrl,
$discoveryJson
);
$sensorValue->publishDiscovery($this->getClient());
$sensorStateUrl = $sensorValue->publishMqqtValue($this->getClient());
$this->getClient()->publish(
self::SENSOR_PREFIX . $sensorValue->getId() . '/heating/state',
$sensorValue->getValue()
);
$this->logger->debug('Stored "' . $sensorValue->getName() . '" in mqqt ' . $sensorStateUrl . ' ' . $sensorValue->getValue());
}
......
......@@ -5,8 +5,10 @@ namespace HeatingControl\State;
/**
* Represents the humidity measurement of a sensor
*/
class HumiditySensorState extends State
abstract class HumiditySensorState extends State
{
const SENSOR_PREFIX = 'homeassistant/sensor/';
const UNIT_OF_MEASUREMENT = '%';
const DEVICE_CLASS = 'humidity';
}
......@@ -7,9 +7,7 @@ namespace HeatingControl\State;
*/
class RelayState extends State
{
const UNIT_OF_MEASUREMENT = 'bool';
const DEVICE_CLASS = 'relay';
const SENSOR_PREFIX = 'homeassistant/switch/';
const DIAGRAM_PUMP_ID = 9;
const DIAGRAM_BURNER_ID = 10;
......@@ -17,6 +15,24 @@ class RelayState extends State
const BURNER_KEY = 'burner';
const PUMP_KEY = 'pump';
public function publishDiscovery($client): string
{
$discoveryJson = json_encode(
[
'name' => $this->getName(),
'state_topic' => $this->getMqqtStateUrl(),
'command_topic' => $this->getMqqtStateUrl()
]
);
$client->publish(
$this->getMqqtDiscoveryUrl(),
$discoveryJson
);
return $this->getMqqtDiscoveryUrl();
}
/**
* Dont direct return a boolean value. RRDTool need a int or string
*/
......
......@@ -3,11 +3,11 @@ declare(strict_types=1);
namespace HeatingControl\State;
use Mosquitto\Client;
abstract class State
{
const UNIT_OF_MEASUREMENT = 'unknown';
const DEVICE_CLASS = 'unknown';
const SENSOR_PREFIX = 'unknown';
/** @var string Im Regelfall eine Temperatur, könnte aber auch z.B. % Luftfeuchtigkeit sein */
protected $value;
......@@ -24,6 +24,36 @@ abstract class State
/** @var bool All Sensors in Database are configured, all new sensors not */
protected $configured = false;
abstract public function publishDiscovery(Client $client);
/**
* Das muss hier raus
*
* @param Client $client
* @return string
*/
public function publishMqqtValue(Client $client): string
{
$client->publish(
$this->getMqqtStateUrl(),
(string)$this->getValue()
);
return $this->getMqqtStateUrl();
}
protected function getMqqtDiscoveryUrl()
{
return static::SENSOR_PREFIX . $this->getId() . '/heating/config';
}
protected function getMqqtStateUrl()
{
return static::SENSOR_PREFIX . $this->getId() . '/heating/state';
}
/**
* @return array
*/
......@@ -133,14 +163,4 @@ abstract class State
$this->setValue($data['value']);
$this->setConfigured($data['configured']);
}
public function getDeviceClass()
{
return static::DEVICE_CLASS;
}
public function getUnit()
{
return static::UNIT_OF_MEASUREMENT;
}
}
......@@ -2,12 +2,31 @@
namespace HeatingControl\State;
use Mosquitto\Client;
/**
* Represents the temperature measurement of a sensor
*/
class TemperaturSensorState extends State
{
const UNIT_OF_MEASUREMENT = '°C';
const SENSOR_PREFIX = 'homeassistant/sensor/';
public function publishDiscovery(Client $client): string
{
$discoveryJson = json_encode(
[
'name' => $this->getName(),
'device_class' => 'temperature',
'state_topic' => $this->getMqqtStateUrl(),
'unit_of_measurement' => '°C'
]
);
$client->publish(
$this->getMqqtDiscoveryUrl(),
$discoveryJson
);
const DEVICE_CLASS = 'temperature';
return $this->getMqqtDiscoveryUrl();
}
}
......@@ -81,10 +81,11 @@ class HeatingManagementServiceTest extends IntegrationTestCase
/**
* @dataProvider expectedHeatingBehavior
*/
public function testUpdateStatus($outsideTemp, $returnTemp, $destTemp, $threshold, $lastBurnerStatus, $burner, $pump)
public function testUpdateStatus($outsideTemp, $returnTemp, $destTemp, $threshold, $lastBurnerStatus, $burnerStatus, $pumpStatus)
{
list(
$sensorService,
/** @var RelayService $relayService */
$relayService,
$nightSettingService,
$mappingService,
......@@ -108,12 +109,12 @@ class HeatingManagementServiceTest extends IntegrationTestCase
$memcachedService->get('10-xx2')->willReturn(null);
$memcachedService->get('burner')->willReturn($lastBurnerStatus);
$memcachedService->get('pump')->willReturn(true);
$memcachedService->set('burner', $burner)->shouldBeCalled();
$memcachedService->set('pump', $pump)->shouldBeCalled();
$memcachedService->set('burner', $burnerStatus)->shouldBeCalled();
$memcachedService->set('pump', $pumpStatus)->shouldBeCalled();
// This is Part of the testet behavior
$relayService->setRelay('gpio01', $burner)->shouldBeCalled();
$relayService->setRelay('gpio02', $pump)->shouldBeCalled();
$relayService->setRelay('gpio01', $burnerStatus)->shouldBeCalled();
$relayService->setRelay('gpio02', $pumpStatus)->shouldBeCalled();
$bcs = new HeatingManagementService(
[
......
<?php
declare(strict_types=1);
namespace HeatingControl\Test\Controller;
use HeatingControl\Service\ShellCommandService;
use HeatingControl\Service\RelayService;
use HeatingControl\Service\ShellCommandService;
use HeatingControl\Test\IntegrationTestCase;
class RelayServiceTest extends IntegrationTestCase
......@@ -22,11 +23,23 @@ class RelayServiceTest extends IntegrationTestCase
public function testsetRelais()
{
$prophet = $this->prophesize(ShellCommandService::class);
$prophet->exec("echo '1' > /sys/class/gpio/gpio21/value")->willReturn(1);
$prophet->exec("echo '1' > /sys/class/gpio/gpio21/value")->willReturn("");
$commandServiceMock = $prophet->reveal();
$relayService = new RelayService($commandServiceMock);
$this->assertTrue($relayService->setRelay('gpio21', RelayService::IO_VALUE_ON));
}
/**
* @expectedException \TypeError
* @expectedExceptionMessage must be of the type bool
*/
public function testsetRelaisWithInvalidStatus()
{
$prophet = $this->prophesize(ShellCommandService::class);
$commandServiceMock = $prophet->reveal();
$relayService = new RelayService($commandServiceMock);
$this->assertNull($relayService->setRelay('gpio21', '1'));
$this->assertNull($relayService->setRelay('gpio21', 1));
$this->assertFalse($relayService->setRelay('gpio21', 3));
}
}
<?php
declare(strict_types=1);
namespace HeatingControl\Test\State;
use HeatingControl\State\TemperaturSensorState;
use PHPUnit\Framework\TestCase;
class TemperaturSensorStateTest extends TestCase
{
// Test late state binding getter
public function testGetDeviceType()
{
$state = new TemperaturSensorState();
$this->assertEquals('temperature', $state->getDeviceClass());
}
public function testGetUnit()
{
$state = new TemperaturSensorState();
$this->assertEquals('°C', $state->getUnit());
}
}
......@@ -4,6 +4,7 @@
- [Last generated temperature graph](images/temp.svg)
- [openhab2 GUI](https://heating.fritz.box:8080)
- [Home Assistant](https://heating.fritz.box:8123)
## API
......
......@@ -8,5 +8,5 @@ php backend/cli.php log-sensors
- log-sensors : Read and import current sensor values
- graph : Generate diagramm of temperatures
- gpio : Get Switch Status of the relais card
- watcher : Look at the mqqt Message Queue and set GPIO relais. This function is used when Openhab want to switch a relay
- watcher : Look at the mqqt Message Queue and set GPIO relais. This function is used when Openhab/Home-assistant want to switch a relay
- burner-control : Lock at the current Temperatures and turn pump/burner on/off. Call this function every minute
# Setup
## home-assistant Software
[Read](https://www.home-assistant.io/docs/installation/raspberry-pi/)
## autostart (optional)
https://www.home-assistant.io/docs/autostart/systemd/
## Add Mqqt Message Queue to home-assistant
Damit hass die Sensoren uns Schalter automatisch erkennt, muss hass mit der Queue verbunden
werden wo die Heizungsdaten landen, dazu wird die `configure.yaml` angepasst.
```yaml
mqtt:
broker: localhost
discovery: true
discovery_prefix: homeassistant
```
**localhost** ist in diesem Fall die Queue die beim einrichten auf dem Raspberry installiert wurde.
## hide single sensors in UI
https://www.home-assistant.io/docs/configuration/customizing-devices/
# Example complete config
```yaml
# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:
# Uncomment this if you are using SSL/TLS, running in Docker container, etc.
# http:
# base_url: example.duckdns.org:8123
# Text to speech
tts:
- platform: google_translate
group: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml
device_tracker:
- platform: fritz
host: 192.168.178.1
vacuum:
- platform: roomba
host: 192.168.178.35
username: 69B8860C82323530
password: ":1:1573226762:Cx3IotVdiWHrHuI"
mqtt:
broker: localhost
discovery: true
discovery_prefix: homeassistant
homeassistant:
customize: !include customize.yaml
```
## customize.yaml
```yaml
device_tracker.retropie:
hidden: true
device_tracker.heating:
hidden: true
device_tracker.libreelec:
hidden: true
device_tracker.samsung_galaxy_s7:
hidden: true
device_tracker.roomba_69b8860c82323530:
hidden: true
device_tracker.android_d3a57407a411bde9:
hidden: true
device_tracker.fritz_repeater:
hidden: true
device_tracker.notebook:
hidden: true
device_tracker.fritz_box:
hidden: true
device_tracker.t_eimers_pc:
hidden: true
binary_sensor.updater:
hidden: true
```
https://www.home-assistant.io/integrations/roomba/
......@@ -38,7 +38,7 @@ rectangle "Explorer 700 Card" as 700 {
}
rectangle "Openhab2" {
[<b>OpenHab2\nUI Port 8080] as oh
[<b>Home Assitant\nOpenHab2\nUI Port 8080] as oh
[switch on/off] as binding
......
Mirror of published files from [notdefine.de Website](https://www.notdefine.de/projects.php?project=heizungssteuerung-mit-raspberry-pi)]
This diff is collapsed.
Heizungssteuerung 2018/2019
Heizungs&shy;steuerung für eine Fuß&shy;boden&shy;heizung mittels Linux (Rasbpian) auf dem Raspberry PI mit openhab Anbindung.
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.